package com.arpa.wms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.IterUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.arpa.core.web.Result;
import com.arpa.ntocc.common.common.cache.PartyCache;
import com.arpa.ntocc.common.common.cache.PartyGroupCache;
import com.arpa.ntocc.common.common.cache.ShipmentCache;
import com.arpa.ntocc.common.common.exception.ServiceException;
import com.arpa.ntocc.common.common.util.CommonUtil;
import com.arpa.ntocc.common.common.util.UserUtil;
import com.arpa.ntocc.common.domain.entity.Shipment;
import com.arpa.ntocc.common.service.IShipmentService;
import com.arpa.oms.domain.entity.*;
import com.arpa.oms.domain.enums.PurchaseTypeEnum;
import com.arpa.oms.domain.vo.PurchaseItemVO;
import com.arpa.oms.domain.vo.PurchaseVo;
import com.arpa.oms.service.*;
import com.arpa.wms.cache.*;
import com.arpa.wms.common.util.OrderIdGeneratorUtil;
import com.arpa.wms.domain.consts.NumConst;
import com.arpa.wms.domain.dto.*;
import com.arpa.wms.domain.entity.*;
import com.arpa.wms.domain.enums.*;
import com.arpa.wms.domain.vo.*;
import com.arpa.wms.mapper.ReceiveMapper;
import com.arpa.wms.mapper.ReceiveRegisterMapper;
import com.arpa.wms.service.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.arpa.wms.domain.consts.CommonConst.LOT_NUMBER;
import static com.arpa.wms.domain.consts.CommonConst.REMARKS_NAME;
import static com.arpa.wms.domain.consts.NumConst.*;
import static com.arpa.wms.domain.consts.TipConst.*;

/**
 * <p>
 * 收货 服务实现类
 * </p>
 *
 * @author LEO
 * @since 2020-08-04
 */
@Service
public class ReceiveServiceImpl extends ServiceImpl<ReceiveMapper, Receive> implements IReceiveService {

    @Autowired
    private ShipmentCache shipmentCache;
    @Lazy
    private final IPutawayItemService iPutawayItemService;
    private final IReceiveItemService iReceiveItemService;
    private final IShipmentService iShipmentService;
    private final IWarehouseShipmentService iWarehouseShipmentService;
    private final IGoodsService iGoodsService;
    @Lazy
    private final IPutawayService iPutawayService;
    private final IPutawayRegisterService iPutawayRegisterService;
    private final IDeliveryService iDeliveryService;
    @Lazy
    private final IDeliveryItemService iDeliveryItemService;
    @Lazy
    @Autowired
    private IOmsSaleReturnService iOmsSaleReturnService;
    @Lazy
    @Autowired
    private IOmsSaleReturnItemService iOmsSaleReturnItemService;
    private final WarehouseCache warehouseCache;
    private final PartyCache partyCache;
    private final SupplierCache supplierCache;
    //private final IReceiveRegisterService iReceiveRegisterService;
    @Resource
    private ReceiveRegisterMapper receiveRegisterMapper;
    private final UnitCache unitCache;
    private final CustomerCache customerCache;
    private final GoodsUnitConversionCache goodsUnitConversionCache;
    private final IInventoryService iInventoryService;
    private final IInventoryRecordService iInventoryRecordService;
    private final IContainerService iContainerService;
    private final IWmsTaskService wmsTaskService;
    private final IReceiveItemService receiveItemService;
    private final PartyGroupCache partyGroupCache;

    private final GoodsCache goodsCache;
    @Autowired
    @Lazy
    private IWmsTaskService taskService;
    @Autowired
    private ImportTempCache importTempCache;

    private final IAllocationService allocationService;
    private final IAllocationItemService allocationItemService;


    @Autowired
    private IGoodsUnitService iGoodsUnitService;
    private final LocationCache locationCache;
    @Autowired
    private BatchRuleCache batchRuleCache;
    @Autowired
    private ISaleService iSaleService;
    @Lazy
    @Autowired
    private IOmsAllocationService omsAllocationService;
    @Lazy
    @Autowired
    private IPurchaseService purchaseService;
    @Autowired
    private IPurchaseItemService purchaseItemService;
    @Lazy
    @Autowired
    private IOmsInboundService inboundService;
    @Autowired
    private IOmsInboundItemService inboundItemService;
    @Autowired
    private IOmsInventoryService inventoryService;
    @Autowired
    @Lazy
    private IOmsSettlementService omsSettlementService;


    @Autowired
    private   IGoodsPnService goodsPnService;
    public ReceiveServiceImpl(IPutawayItemService iPutawayItemService,
                              IPutawayRegisterService iPutawayRegisterService,
                              IPutawayService iPutawayService,
                              IDeliveryService iDeliveryService,
                              IDeliveryItemService iDeliveryItemService,
                              WarehouseCache warehouseCache,
                              IGoodsUnitConversionService iGoodsUnitConversionService,
                              PartyCache partyCache,
                              IReceiveRegisterService iReceiveRegisterService,
                              SupplierCache supplierCache,
                              IReceiveItemService iReceiveItemService,
                              UnitCache unitCache,
                              CustomerCache customerCache,
                              GoodsUnitConversionCache goodsUnitConversionCache,
                              IInventoryService iInventoryService,
                              IInventoryRecordService iInventoryRecordService,
                              IContainerService iContainerService,
                              IWmsTaskService wmsTaskService,
                              IReceiveItemService receiveItemService,
                              IShipmentService iShipmentService,
                              IWarehouseShipmentService IWarehouseShipmentService,
                              IGoodsService iGoodsService,
                              GoodsCache goodsCache,
                              PartyGroupCache partyGroupCache,
                              IAllocationService allocationService,
                              LocationCache locationCache,
                              IAllocationItemService allocationItemService
    ) {
        this.iPutawayItemService = iPutawayItemService;
        this.iPutawayRegisterService = iPutawayRegisterService;
        this.iPutawayService = iPutawayService;
        this.iDeliveryService = iDeliveryService;
        this.iDeliveryItemService = iDeliveryItemService;
        this.iOmsSaleReturnService = iOmsSaleReturnService;
        this.iOmsSaleReturnItemService = iOmsSaleReturnItemService;
        this.warehouseCache = warehouseCache;
        this.partyCache = partyCache;
        this.supplierCache = supplierCache;
        //this.iReceiveRegisterService = iReceiveRegisterService;
        this.iReceiveItemService = iReceiveItemService;
        this.unitCache = unitCache;
        this.customerCache = customerCache;
        this.goodsUnitConversionCache = goodsUnitConversionCache;
        this.iInventoryService = iInventoryService;
        this.iInventoryRecordService = iInventoryRecordService;
        this.iContainerService = iContainerService;
        this.wmsTaskService = wmsTaskService;
        this.receiveItemService = receiveItemService;
        this.partyGroupCache = partyGroupCache;
        this.goodsCache = goodsCache;
        this.allocationService = allocationService;
        this.allocationItemService = allocationItemService;
        this.iWarehouseShipmentService = IWarehouseShipmentService;
        this.iShipmentService = iShipmentService;
        this.iGoodsService = iGoodsService;
        this.locationCache = locationCache;
    }


    /**
     * 保存
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public boolean save(Receive entity) {
        entity.setGroupCode(UserUtil.getBranchCode());
        entity.setCreatedBy(UserUtil.getCode());
        entity.setCreatedName(partyCache.translate(UserUtil.getCode()));
        entity.setGmtCreated(LocalDateTime.now());
        entity.setModifiedBy(UserUtil.getCode());
        entity.setModifiedName(partyCache.translate(UserUtil.getCode()));
        entity.setGmtModified(LocalDateTime.now());
        boolean flag = super.save(entity);
        if (flag) {
            this.saveItem(entity);
        }

        return flag;
    }

    /**
     * 保存收货明细
     *
     * @param entity 收货单
     */
    private void saveItem(Receive entity) {
        StringBuilder data = new StringBuilder();
        String receiveCode = entity.getCode();
        data.append("INSERT INTO `wms_receive_item`(`id`, `code`,`group_code`, `receive_code`, `goods_code`, `good_code`, `goods_name`, `goods_bar_code`, `spec`, " +
                "`expiration_quantity`, `goods_unit`, `goods_unit_name`, `basic_unit`, `basic_unit_name`, `plan_quantity` , `status`, `remarks`, `created_by`, `created_name`, `gmt_created`, `modified_by`, " +
                " `modified_name`, `gmt_modified`, `unit_convert_quantity`, `source_item_code`, `delivery_item_code`, `compose_good_code`) values ");
        String sql = entity.getReceiveItemList().stream().map(r -> {
            r.setStatus(ReceiveStatusEnum.NEW.getValue());
            r.setReceiveCode(receiveCode);
            r.setDeleted(0);
            return makeInsertValuesString(r, entity.getCode(), entity.getCreatedBy(), entity.getCreatedName());
        }).collect(Collectors.joining(","));
        data.append(sql);
        this.baseMapper.insertDataBySql(data.toString());
    }


    /**
     * 构建sql 添加语句
     */
    private StringBuilder makeInsertValuesString(final ReceiveItem receiveItem, final String receiveCode, final String createdBy, final String createdName) {
        StringBuilder data = new StringBuilder();
        data.append("(");
        //id
        data.append("default,");
        //code
        data.append("'").append(IdUtil.simpleUUID()).append("',");
        //group_code
        data.append("'").append(receiveItem.getGroupCode()).append("',");
        //receiveCode
        data.append("'").append(receiveCode).append("',");
        //goods_code
        data.append("'").append(receiveItem.getGoodsCode()).append("',");
        //good_code
        data.append("'").append(receiveItem.getGoodCode()).append("',");
        //goods_name
        data.append("'").append(receiveItem.getGoodsName()).append("',");
        //goods_bar_code
        data.append("'").append(receiveItem.getGoodsBarCode()).append("',");
        //spec
        data.append("'").append(receiveItem.getSpec()).append("',");
        //expiration_quantity
        data.append("'").append(ObjectUtil.defaultIfNull(receiveItem.getExpirationQuantity(), BigDecimal.ONE)).append("',");
        //goods_unit
        data.append("'").append(receiveItem.getGoodsUnit()).append("',");
        //goods_unit_name
        data.append("'").append(receiveItem.getGoodsUnitName()).append("',");
        //basic_unit
        data.append("'").append(receiveItem.getBasicUnit()).append("',");
        //basic_unit_name
        data.append("'").append(receiveItem.getBasicUnitName()).append("',");
        //plan_quantity
        data.append(receiveItem.getPlanQuantity()).append(",");
        //status
        data.append("'").append(receiveItem.getStatus()).append("',");
        //Remarks
        data.append("'").append(StrUtil.nullToDefault(receiveItem.getRemarks(), StrUtil.EMPTY)).append("',");
        //created_by
        data.append("'").append(createdBy).append("',");
        //createdName
        data.append("'").append(createdName).append("',");
        //gmt_created
        data.append("'").append(LocalDateTime.now()).append("',");
        //modified_by
        data.append("'").append(createdBy).append("',");
        //modifiedName
        data.append("'").append(createdName).append("',");
        //gmt_modified
        data.append("'").append(LocalDateTime.now()).append("',");
        // unitConvertQuantity
        data.append("'").append(receiveItem.getUnitConvertQuantity()).append("',");
        // source_item_code
        data.append("'").append(StrUtil.nullToDefault(receiveItem.getSourceItemCode(), StrUtil.EMPTY)).append("',");
        // delivery_item_code
        data.append("'").append(StrUtil.nullToDefault(receiveItem.getDeliveryItemCode(), StrUtil.EMPTY)).append("',");
        // compose_good_code
        data.append("'").append(StrUtil.nullToDefault(receiveItem.getComposeGoodCode(), StrUtil.EMPTY)).append("'");
        data.append(")");
        return data;
    }


    /**
     * 根据code更新实体
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public int updateByCode(Receive entity) {
        String groupCode = entity.getGroupCode();
        if (StringUtils.isBlank(entity.getModifiedBy())) {
            entity.setModifiedBy(UserUtil.getCode());
            entity.setModifiedName(partyCache.translate(UserUtil.getCode()));
            entity.setGmtModified(LocalDateTime.now());
        }
        //删除所有明细
        iReceiveItemService.remove(new QueryWrapper<ReceiveItem>().lambda().eq(ReceiveItem::getReceiveCode, entity.getCode()).eq(ReceiveItem::getGroupCode,groupCode));
        //保存明细
        if (entity.getReceiveItemList().size() == 0) {
            List<ReceiveItem> receiveItems = iReceiveItemService.queryAllListByReceiveCode(entity.getCode(),groupCode);
            entity.setReceiveItemList(receiveItems);
        }
        this.saveItem(entity);
        int num = baseMapper.update(entity, new QueryWrapper<Receive>().lambda().eq(Receive::getCode, entity.getCode()).eq(Receive::getGroupCode,groupCode));

        //如果状态是收货完成，就生成一个上架单；
        String status = entity.getStatus();
        if (StringUtils.isNotBlank(status) && ReceiveStatusEnum.FINISH_RECEIVE.getValue().equals(status)) {
            this.savePutaway(entity);
        }
        return num;
    }


    /**
     * 查询列表
     *
     * @param receiveDTO
     * @return
     */
    @Override
    public List<ReceiveVO> queryList(ReceiveDTO receiveDTO) {
        //排序字段名需要驼峰转数据库下划线类型字段名
        if (StringUtils.isNotEmpty(receiveDTO.getSortField())) {
            receiveDTO.setSortField(CommonUtil.camel2Underline(receiveDTO.getSortField()));
        }
        if (receiveDTO.getSortField().equals("warehouse_name")){
            receiveDTO.setSortField("a.warehouse_code");
        }
        if (receiveDTO.getSortField().equals("issue")){
            receiveDTO.setSortField("a.is_issue");
        }  if (receiveDTO.getSortField().equals("type_name")){
            receiveDTO.setSortField("a.type");
        }
        List<ReceiveVO> receiveVOList = baseMapper.queryList(receiveDTO);
        //针对销售退货入库，新增面单号
//        addWaybillCode(receiveVOList);
        if (IterUtil.isNotEmpty(receiveVOList)) {
            receiveVOList.forEach(r -> {
                ReceiveStatusEnum enumByValue = ReceiveStatusEnum.getEnumByValue(r.getStatus());
                if (null != enumByValue) {
                    r.setStatusName(enumByValue.getDesc());
                }
                r.setTypeName(ReceiveTypeEnum.translate(r.getType()));
                r.setWarehouseName(warehouseCache.translate(r.getWarehouseCode()));
            });
        }
        return receiveVOList;
    }

    /**
     * 销售退货入库，新增面单号
     *
     * @param receiveVOList
     * @return
     */
    private void addWaybillCode(List<ReceiveVO> receiveVOList) {

        List<ReceiveVO> returnReceiveList = receiveVOList.stream()
                .filter(e -> ReceiveTypeEnum.RETURN.getValue().equals(e.getType()))
                .collect(Collectors.toList());
        if (IterUtil.isEmpty(returnReceiveList)) {
            return;
        }
        String groupCode = UserUtil.getBranchCode();
        //根据来源单号获取面单信息
        Set<String> sourceCodes = returnReceiveList.stream().map(ReceiveVO::getPreDeliveryOrderCode).collect(Collectors.toSet());
        List<Delivery> deliveryList = iDeliveryService.list(new LambdaQueryWrapper<Delivery>()
                .select(Delivery::getDeliveryOrderCode, Delivery::getWaybillCode)
                .eq(Delivery::getGroupCode, groupCode)
                .in(Delivery::getDeliveryOrderCode, sourceCodes)
        );
        deliveryList = deliveryList.stream().filter(distinctByKey(Delivery::getDeliveryOrderCode)).collect(Collectors.toList());
        Map<String,Delivery> deliveryMap = deliveryList.stream().collect(Collectors.toMap(Delivery::getDeliveryOrderCode, e -> e));
        receiveVOList.stream().forEach(e -> {
            if (ReceiveTypeEnum.RETURN.getValue().equals(e.getType())) {
                if (MapUtil.isNotEmpty(deliveryMap) && null != MapUtil.get(deliveryMap,e.getPreDeliveryOrderCode(),Delivery.class)) {
                    e.setWaybillCode(MapUtil.get(deliveryMap,e.getPreDeliveryOrderCode(),Delivery.class).getWaybillCode());
                }
            }
        });

    }

    private static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
        Map<Object, Boolean> seen = new ConcurrentHashMap<>();
        return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
    }

    /**
     * 查询合计,包含总数
     *
     * @param receiveDTO
     * @return
     */
    @Override
    public ReceiveVO queryListSum(ReceiveDTO receiveDTO) {
        if (IterUtil.isEmpty(receiveDTO.getShipmentCodes())) {
            return null;
        }

        StringBuilder sb = new StringBuilder();
        sb.append("SELECT ");
        receiveDTO.getShipmentCodes().forEach(e -> {
            sb.append("(SELECT count(*) AS total FROM `wms_receive`  WHERE 1=1 ");
            if (ObjectUtil.isNotEmpty(receiveDTO.getDeleted())) {
                sb.append(" and `is_deleted` = ").append(receiveDTO.getDeleted());
            } else {
                sb.append(" and `is_deleted` =  0 " );
            }
            if (StrUtil.isNotEmpty(receiveDTO.getWarehouseCode())) {
                sb.append(" and `warehouse_code` ='").append(receiveDTO.getWarehouseCode()).append("'");
            }
            if (StrUtil.isNotEmpty(receiveDTO.getGroupCode())) {
                sb.append(" and `group_code` ='").append(receiveDTO.getGroupCode()).append("'");
            }
            sb.append(" and `shipment_code` =  '").append(e).append("'");

            if (StrUtil.isNotEmpty(receiveDTO.getCode())) {
                sb.append(" and `code` like'%").append(receiveDTO.getCode()).append("%'");
            }
            if (StrUtil.isNotEmpty(receiveDTO.getSourceCode())) {
                sb.append(" and `source_code` ='").append(receiveDTO.getSourceCode()).append("'");
            }
            if (StrUtil.isNotEmpty(receiveDTO.getShipmentName())) {
                sb.append(" and `shipment_name` like'%").append(receiveDTO.getShipmentName()).append("%'");
            }
            if (StrUtil.isNotEmpty(receiveDTO.getType())) {
                sb.append(" and `type` ='").append(receiveDTO.getType()).append("'");
            }
            if (StrUtil.isNotEmpty(receiveDTO.getSupplierCode())) {
                sb.append(" and `supplier_code` ='").append(receiveDTO.getSupplierCode()).append("'");
            }


            if (StrUtil.isNotEmpty(receiveDTO.getCustomerCode())) {
                sb.append(" and `customer_code` ='").append(receiveDTO.getCustomerCode()).append("'");
            }
            if (ObjectUtil.isNotEmpty(receiveDTO.getGmtPlanArrival())) {
                sb.append(" and `gmt_plan_arrival` ='").append(receiveDTO.getGmtPlanArrival()).append("'");
            }

            if (StrUtil.isNotEmpty(receiveDTO.getStatus())) {
                sb.append(" and `status` ='").append(receiveDTO.getStatus()).append("'");
            }
            if (ObjectUtil.isNotEmpty(receiveDTO.getIssue())) {
                sb.append(" and `is_issue` =").append(receiveDTO.getIssue());
            }
            if (StrUtil.isNotEmpty(receiveDTO.getRemarks())) {
                sb.append(" and `remarks` ='").append(receiveDTO.getRemarks()).append("'");
            }
            if (StrUtil.isNotEmpty(receiveDTO.getId())) {
                sb.append(" and `id` =").append(receiveDTO.getId());
            }
            if (StrUtil.isNotEmpty(receiveDTO.getCreatedBy())) {
                sb.append(" and `created_by` ='").append(receiveDTO.getCreatedBy()).append("'");
            }

            if (ObjectUtil.isNotEmpty(receiveDTO.getGmtCreated())) {
                sb.append(" and `gmt_created` ='").append(receiveDTO.getGmtCreated()).append("'");
            }
            if (ObjectUtil.isNotEmpty(receiveDTO.getModifiedBy())) {
                sb.append(" and `modified_by` ='").append(receiveDTO.getModifiedBy()).append("'");
            }
            if (StrUtil.isNotEmpty(receiveDTO.getModifiedName())) {
                sb.append(" and `modified_name` ='").append(receiveDTO.getModifiedName()).append("'");
            }
            if (StrUtil.isNotEmpty(receiveDTO.getCreatedName())) {
                sb.append(" and `created_name` ='").append(receiveDTO.getCreatedName()).append("'");
            }
            if (ObjectUtil.isNotEmpty(receiveDTO.getGmtPlanArrivalStart()) && ObjectUtil.isNotEmpty(receiveDTO.getGmtPlanArrivalEnd())) {
                sb.append(" and `gmt_plan_arrival` between '").append(receiveDTO.getGmtPlanArrivalStart()).append("' and '").append(receiveDTO.getGmtPlanArrivalEnd()).append("'");
            }

            if (ObjectUtil.isNotEmpty(receiveDTO.getGmtCreatedStart()) && ObjectUtil.isNotEmpty(receiveDTO.getGmtCreatedEnd())) {
                sb.append(" and `gmt_created` between '").append(receiveDTO.getGmtCreatedStart()).append("' and '").append(receiveDTO.getGmtCreatedEnd()).append("'");
            }
            if (ObjectUtil.isNotEmpty(receiveDTO.getGmtModifiedStart()) && ObjectUtil.isNotEmpty(receiveDTO.getGmtModifiedEnd())) {
                sb.append(" and `gmt_modified` between '").append(receiveDTO.getGmtModifiedStart()).append("' and '").append(receiveDTO.getGmtModifiedEnd()).append("'");
            }

            if (ObjectUtil.isNotEmpty(receiveDTO.getTotalQuantityStart()) && ObjectUtil.isNotEmpty(receiveDTO.getTotalQuantityEnd())) {
                sb.append(" and `total_quantity` between '").append(receiveDTO.getTotalQuantityStart()).append(" and ").append(receiveDTO.getTotalQuantityEnd());
            }
            if (ObjectUtil.isNotEmpty(receiveDTO.getReceivedQuantityStart()) && ObjectUtil.isNotEmpty(receiveDTO.getReceivedQuantityEnd())) {
                sb.append(" and `received_quantity` between ").append(receiveDTO.getReceivedQuantityStart()).append("' and ").append(receiveDTO.getReceivedQuantityEnd());
            }
            if (ObjectUtil.isNotEmpty(receiveDTO.getPutawayQuantityStart()) && ObjectUtil.isNotEmpty(receiveDTO.getPutawayQuantityEnd())) {
                sb.append(" and `putaway_quantity` between ").append(receiveDTO.getPutawayQuantityStart()).append(" and ").append(receiveDTO.getPutawayQuantityEnd());
            }



            sb.append(") +");
        });
        sb.replace(sb.length() - 1, sb.length(), "");
        sb.append("AS total ");

        return this.baseMapper.queryListSum(sb.toString());
    }

    /**
     * 查询合计,包含总数
     *
     * @param receiveDTO
     * @return
     */
    @Override
    public ReceiveVO queryListTotal(ReceiveDTO receiveDTO){
        return this.baseMapper.queryListTotal(receiveDTO);
    }

    /**
     * 获取详情
     *
     * @param code code
     * @return
     */
    @Override
    public ReceiveVO getOneVOByCode(String code) {
        Receive receive = getOne(new QueryWrapper<Receive>().lambda().eq(Receive::getCode, code).eq(Receive::getGroupCode,UserUtil.getBranchCode()));
        if (receive == null) {
            throw new ServiceException("收货单：" + code + "不存在！");
        }

        ReceiveVO receiveVO = new ReceiveVO();
        receiveVO.setGroupCode(UserUtil.getBranchCode());
        BeanUtils.copyProperties(receive, receiveVO);
        // 主单翻译
        receiveVO.setWarehouseName(warehouseCache.translate(receiveVO.getWarehouseCode()));
        receiveVO.setGroupName(partyCache.translate(receiveVO.getGroupCode()));
        receiveVO.setShipmentName(shipmentCache.translate(receiveVO.getShipmentCode()));
        receiveVO.setCustomerName(customerCache.translate(receiveVO.getCustomerCode()));
        receiveVO.setSupplierName(supplierCache.translate(receiveVO.getSupplierCode()));
        receiveVO.setTypeName(ReceiveTypeEnum.translate(receiveVO.getType()));

        ReceiveItemDTO receiveItemDTO = new ReceiveItemDTO();
        receiveItemDTO.setReceiveCode(code);
        List<ReceiveItemVO> receiveItemVOList;
        // 查询明细， 由于退货单需要关联订单明细查询，所以需要判断一下
        if (StrUtil.equals(receiveVO.getType(), ReceiveTypeEnum.RETURN.getValue())) {
            receiveItemVOList = iReceiveItemService.queryReturnList(receiveItemDTO);
        } else {
            //获取收货单明细信息
            receiveItemVOList = iReceiveItemService.queryList(receiveItemDTO);
        }
        // 明细翻译
        receiveItemVOList.forEach(r -> {
            List<GoodsUnitConversion> goodsUnitConversions = goodsUnitConversionCache.get(r.getGoodsCode());
            r.setGoodsUnitConversionList(goodsUnitConversions);
            r.setGoodsUnitName(unitCache.translate(r.getGoodsUnit()));
            r.setBasicUnitName(unitCache.translate(r.getBasicUnit()));
        });

        receiveVO.setReceiveItemVOList(receiveItemVOList);
        return receiveVO;
    }


    /**
     * 批量删除
     *
     * @param codes
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void batchdel(List<String> codes) {
        for (String code : codes) {
            this.update(new UpdateWrapper<Receive>().lambda().eq(Receive::getCode, code).eq(Receive::getGroupCode,UserUtil.getBranchCode())
                    .set(Receive::getDeleted, 1));
        }
    }


    /**
     * 作废
     *
     * @param codes
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void blank(List<String> codes) {
        for (String code : codes) {
            // 对新增且未删除的收货单进行作废操作
            this.update(new UpdateWrapper<Receive>().lambda().eq(Receive::getCode, code).eq(Receive::getGroupCode,UserUtil.getBranchCode())
                    .eq(Receive::getStatus, ReceiveStatusEnum.NEW.getValue())
                    .eq(Receive::getDeleted, "0")
                    .set(Receive::getStatus, ReceiveStatusEnum.CANCEL.getValue()));
            // 对退货单需要释放库存, 逻辑调整，新增状态的退货单不会锁订单数量，所以不会有释放数量的操作
            //this.releaseLock(code);
        }
    }

    /**
     * 生成上架单
     *
     * @param entity 收货单
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void savePutaway(Receive entity) {
        String groupCode = entity.getGroupCode();
        //判断能否生成上架单
        boolean flag = validateCanPutaway(entity);
        if (flag) {
            Putaway putaway = new Putaway();

            putaway.setCode(OrderIdGeneratorUtil.generatorOrderId(OrderIdGeneratorUtil.OrderTypeEnum.PA, entity.getShipmentCode(),groupCode));

            putaway.setSupplierCode(entity.getSupplierCode());
            putaway.setWarehouseCode(entity.getWarehouseCode());
            putaway.setReceiveCode(entity.getCode());
            putaway.setShipmentCode(entity.getShipmentCode());
            putaway.setShipmentName(entity.getShipmentName());
            putaway.setGroupCode(entity.getGroupCode());

            putaway.setTotalQuantity(entity.getReceivedQuantity());
            putaway.setPutawayQuantity(BigDecimal.ZERO);

            //待上架的状态
            putaway.setStatus(PutawayStatusEnum.WAITING_PUTAWAY.getValue());
            if (StringUtils.isNotBlank(entity.getRemarks())) {
                putaway.setRemarks(entity.getRemarks());
            }

            iPutawayService.save(putaway);

            // 处理上架明细、未确认的上架确认单
            ReceiveItemDTO receiveItemDTO = new ReceiveItemDTO();
            receiveItemDTO.setReceiveCode(entity.getCode());
            receiveItemDTO.setGroupCode(groupCode);
            List<ReceiveItemWithRegisterVO> receiveRegisterList = iReceiveItemService.queryItemWithRegisterList(receiveItemDTO);
            //获取推荐库位
            getRecommendLocation(receiveRegisterList,groupCode);
            List<PutawayItem> putawayItems = new ArrayList<>();
            List<PutawayRegister> putawayRegisters = new ArrayList<>();

            // 生成上架item、register（未确认状态register）
            for (ReceiveItemWithRegisterVO e : receiveRegisterList) {
                //收货批次（以及上架单号）为空时，可以进行上架
                if (StrUtil.isBlank(e.getPutawayCode())) {
                    generatePutawayItemAndRegister(putaway, e, putawayItems, putawayRegisters);
                    //回写收货登记上架单号
                    //iReceiveRegisterService.update(new UpdateWrapper<ReceiveRegister>().lambda().eq(ReceiveRegister::getCode,e.getRegisterCode())
                    //        .set(ReceiveRegister::getPutawayCode,putaway.getCode()));
                    receiveRegisterMapper.update(null,new UpdateWrapper<ReceiveRegister>().lambda()
                            .eq(ReceiveRegister::getGroupCode,groupCode)
                            .eq(ReceiveRegister::getCode,e.getRegisterCode())
                            .set(ReceiveRegister::getPutawayCode,putaway.getCode()));
                }
            }
            //根据上架明细的数据更新上架主单计划数量
            BigDecimal totalQuantity = putawayItems.stream().map(PutawayItem::getReceivedQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
           iPutawayService.update(new UpdateWrapper<Putaway>().lambda().eq(Putaway::getCode,putaway.getCode()).set(Putaway::getTotalQuantity,totalQuantity).eq(Putaway::getGroupCode,groupCode));
            // 批量保存上架明细 XYTODO TODO 优化批量保存操作
            iPutawayItemService.saveBatch(putawayItems);
            // 批量保存上架确认单
            iPutawayRegisterService.saveBatch(putawayRegisters);
        }
    }

    /**
     * 生成上架单并回填收货登记信息
     *
     * @param entity 收货单
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void savePutawayAndReturnRegisterInfo(Receive entity) {
        String groupCode = entity.getGroupCode();
        //判断能否生成上架单
        boolean flag = validateCanPutaway(entity);
        if (flag) {
            Putaway putaway = new Putaway();

            putaway.setCode(OrderIdGeneratorUtil.generatorOrderId(OrderIdGeneratorUtil.OrderTypeEnum.PA, entity.getShipmentCode(), groupCode));

            putaway.setSupplierCode(entity.getSupplierCode());
            putaway.setWarehouseCode(entity.getWarehouseCode());
            putaway.setReceiveCode(entity.getCode());
            putaway.setShipmentCode(entity.getShipmentCode());
            putaway.setShipmentName(entity.getShipmentName());
            putaway.setGroupCode(entity.getGroupCode());
            //生成上架数量
            BigDecimal receiveQuantity = BigDecimal.ZERO;
            //List<ReceiveRegister> receiveRegisters = iReceiveRegisterService.list(new QueryWrapper<ReceiveRegister>()
            //        .lambda()
            //        .eq(ReceiveRegister::getReceiveCode,entity.getCode())
            //        .eq(ReceiveRegister::getDeleted,"0"));

            List<ReceiveRegister> receiveRegisters = receiveRegisterMapper.selectList(new QueryWrapper<ReceiveRegister>()
                    .lambda()
                    .eq(ReceiveRegister::getGroupCode,groupCode)
                    .eq(ReceiveRegister::getReceiveCode,entity.getCode())
                    .eq(ReceiveRegister::getDeleted,"0"));
            for (ReceiveRegister receiveRegister : receiveRegisters) {
                if (StrUtil.isEmpty(receiveRegister.getPutawayCode())) {
                    receiveQuantity = receiveQuantity.add(receiveRegister.getReceivedQuantity());
                }
            }
            putaway.setTotalQuantity(receiveQuantity);
            putaway.setPutawayQuantity(BigDecimal.ZERO);

            //待上架的状态
            putaway.setStatus(PutawayStatusEnum.WAITING_PUTAWAY.getValue());
            if (StringUtils.isNotBlank(entity.getRemarks())) {
                putaway.setRemarks(entity.getRemarks());
            }
            iPutawayService.save(putaway);

            // 处理上架明细、未确认的上架确认单
            ReceiveItemDTO receiveItemDTO = new ReceiveItemDTO();
            receiveItemDTO.setReceiveCode(entity.getCode());
            receiveItemDTO.setGroupCode(groupCode);
            List<ReceiveItemWithRegisterVO> receiveRegisterList = iReceiveItemService.queryItemWithRegisterList(receiveItemDTO);
            //获取推荐库位
            getRecommendLocation(receiveRegisterList,groupCode);
            List<PutawayItem> putawayItems = new ArrayList<>();
            List<PutawayRegister> putawayRegisters = new ArrayList<>();

            // 生成上架item、register（未确认状态register）
            for (ReceiveItemWithRegisterVO e : receiveRegisterList) {
                if (StrUtil.isEmpty(e.getPutawayCode())) {
                    generatePutawayItemAndRegister(putaway, e, putawayItems, putawayRegisters);
                    //回写收货登记上架单号
                    //iReceiveRegisterService.update(new UpdateWrapper<ReceiveRegister>().lambda().eq(ReceiveRegister::getCode,e.getRegisterCode())
                    //        .set(ReceiveRegister::getPutawayCode,putaway.getCode()));

                    receiveRegisterMapper.update(null,new UpdateWrapper<ReceiveRegister>().lambda().eq(ReceiveRegister::getCode,e.getRegisterCode())
                            .set(ReceiveRegister::getPutawayCode,putaway.getCode()).eq(ReceiveRegister::getGroupCode,groupCode));
                }
            }

            // 批量保存上架明细 XYTODO TODO 优化批量保存操作
            iPutawayItemService.saveBatch(putawayItems);
            // 批量保存上架确认单
            iPutawayRegisterService.saveBatch(putawayRegisters);
        }
    }

    /**
     * 获取推荐库位
     *
     * @param receiveRegisterList
     */
    private void getRecommendLocation(List<ReceiveItemWithRegisterVO> receiveRegisterList,String groupCode) {
        List<String> goodsCodes = receiveRegisterList.stream().map(info -> { return info.getGoodsCode(); }).collect(Collectors.toList());
        InventoryDTO inventoryDTO = new InventoryDTO();
        inventoryDTO.setGoodsCodes(goodsCodes);
        //通过登录选择的仓库过滤数据
        inventoryDTO.setWarehouseCodes(UserUtil.getAuthorizeWarehouseCodes());
        //登录仓库授权的入驻商过滤
        inventoryDTO.setShipmentCodes(UserUtil.getAuthorizeShipmentCodes());
        inventoryDTO.setGroupCode(groupCode);
        List<InventoryVO> inventoryVOS = iInventoryService.getRecommondLocation(inventoryDTO);
        Map<String, List<InventoryVO>> listMap = inventoryVOS.stream().collect(Collectors.groupingBy(InventoryVO::getGoodsCode));

        receiveRegisterList.stream().forEach(e -> {
            if (listMap.containsKey(e.getGoodsCode())) {
                List<InventoryVO> inventoryVOList = MapUtil.get(listMap, e.getGoodsCode(), List.class);
                if (IterUtil.isEmpty(inventoryVOList)) {
                    e.setRecommendLocation(StrUtil.EMPTY);
                } else {
                    Location location = locationCache.getObj(inventoryVOList.get(NUM_ZERO).getLocation());
                    e.setRecommendLocation(location.getCode());
                }
            }
        });
    }

    /**
     * 校验是否可以形成上架单
     *
     * @param entity 收货单
     */
    public boolean validateCanPutaway(Receive entity) {
        ReceiveItemDTO receiveItemDTO = new ReceiveItemDTO();
        receiveItemDTO.setReceiveCode(entity.getCode());
        receiveItemDTO.setGroupCode(entity.getGroupCode());
        List<ReceiveItemWithRegisterVO> receiveRegisterList = iReceiveItemService.queryItemWithRegisterList(receiveItemDTO);
        for(ReceiveItemWithRegisterVO r : receiveRegisterList) {
            //上架单号为空时并且收货数量大于0时，可以进行上架
            if (ObjectUtil.isNull(r.getReceivedQuantity())) {
                continue;
            }
            if (StrUtil.isBlank(r.getPutawayCode()) && (r.getReceivedQuantity().compareTo(BigDecimal.ZERO) > NUM_ZERO)) {
                return true;
            }
        }
        return false;
    }
    /**
     * @param putaway:
     * @param item:
     * @return com.arpa.wms.domain.entity.PutawayItem
     * @description 根据上架单、收货明细-登记信息 生成上架明细、上架确认单（未确认状态）
     * @author xuyang
     * @date 2020/9/26 17:18
     **/
    public void generatePutawayItemAndRegister(Putaway putaway, ReceiveItemWithRegisterVO item, List<PutawayItem> items, List<PutawayRegister> registers) {
        // 用于记录托盘推荐库位，可减少相同托盘上的商品重复查找推荐库位的情况，也可避免推荐了不同库位的情况 key:容器号， value：推荐库位
        Map<String, Location> recommendedLocationForPalletInfo = new HashMap<>();
        // 推荐库位
        Location recommendedLocation = null;
        String recommendedLocationCode = null;
        String recommendedSpaceCode = null;

        // 收货数量>0 才生成上架单明细
        BigDecimal receivedQuantity = ObjectUtil.defaultIfNull(item.getReceivedQuantity(), item.getItemReceivedQuantity());
        if (receivedQuantity.compareTo(BigDecimal.ZERO) > NUM_ZERO) {
            recommendedLocationCode = null;
            recommendedSpaceCode = null;
            PutawayItem putawayItem = new PutawayItem();
            BeanUtil.copyProperties(item, putawayItem);
            putawayItem.setCode(IdUtil.simpleUUID());
            putawayItem.setPutawayCode(putaway.getCode());
            putawayItem.setReceiveItemCode(item.getCode());
            putawayItem.setReceiveRegisterCode(item.getRegisterCode());
            putawayItem.setStatus(PutawayItemStatusEnum.WAITING.getValue());
            putawayItem.setGroupCode(UserUtil.getBranchCode());

            // 计算推荐库位，如果是托盘，则根据仓库推荐库位
           /* if (StrUtil.isNotBlank(item.getContainerBarCode()) && ContainerTypeEnum.PALLET.getValue().equals(item.getContainerType())) {
                // 如果是托盘且之前有推荐过，则使用之前的，因为同一托盘上的商品一定会放到相同库位上的
                if (recommendedLocationForPalletInfo.containsKey(item.getContainerBarCode())) {
                    recommendedLocation = recommendedLocationForPalletInfo.get(item.getContainerBarCode());
                    recommendedLocationCode = recommendedLocation.getCode();
                    recommendedSpaceCode = recommendedLocation.getSpaceCode();
                } else {
                    // 之前没有记录，则计算出推荐库位
                    recommendedLocation = warehouseInternalStrategy.recommendedLocationForPallet(putaway.getWarehouseCode());
                    if (recommendedLocation != null) {
                        recommendedLocationCode = recommendedLocation.getCode();
                        recommendedSpaceCode = recommendedLocation.getSpaceCode();
                        recommendedLocationForPalletInfo.put(item.getContainerBarCode(), recommendedLocation);
                    }
                }
            } else {
                // 如果不是托盘，则使用普通计算获取推荐库位
                recommendedLocation = warehouseInternalStrategy.simpleRecommendedLocation(putaway.getWarehouseCode(), item);
                if (recommendedLocation != null) {
                    recommendedLocationCode = recommendedLocation.getCode();
                    recommendedSpaceCode = recommendedLocation.getSpaceCode();
                }
            }

            putawayItem.setRecommendLocation(recommendedLocationCode);*/

            // 生成上架register（未确认状态）
            PutawayRegister register = new PutawayRegister();
            BeanUtil.copyProperties(putawayItem, register);
            register.setCode(IdUtil.simpleUUID());
            register.setStatus(PutawayItemStatusEnum.WAITING.getValue());
            register.setPutawayItemCode(putawayItem.getCode());
//            register.setRecommendLocation(recommendedLocationCode);
            // 默认情况下“未确认”状态上架确认单库位使用“推荐库位”
            register.setLocation(recommendedLocationCode);
            register.setSpaceCode(recommendedSpaceCode);
            register.setDeleted(NUM_ZERO);
            register.setRemarks(StrUtil.EMPTY);
            // 上架数量
            register.setPutawayQuantity(BigDecimal.ZERO);

            items.add(putawayItem);
            registers.add(register);
        }
    }

    /**
     * 本地保存
     *
     * @param entity
     * @return
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public Result localSave(Receive entity) {
        // 单据状态为“新增”
        entity.setStatus(ReceiveStatusEnum.NEW.getValue());
        entity.setGroupCode(UserUtil.getBranchCode());
        return saveOrCheckReceiveInfos(entity);
    }

    /**
     * 提交收货单
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result check(Receive entity) {
        // 单据状态为“待收货”
        entity.setStatus(ReceiveStatusEnum.WAITING_RECEIVE.getValue());
        entity.setGroupCode(UserUtil.getBranchCode());
        return saveOrCheckReceiveInfos(entity);
    }

    /**
     * 根据code更新入库单信息
     *
     * @param entity
     * @return
     */
    @Override
    public int updateEntityByCode(Receive entity) {
        if (StringUtils.isBlank(entity.getModifiedBy())) {
            entity.setModifiedBy(UserUtil.getCode());
            entity.setModifiedName(partyCache.translate(UserUtil.getCode()));
        }
        return baseMapper.update(entity, new QueryWrapper<Receive>().lambda().eq(Receive::getCode, entity.getCode()).eq(Receive::getGroupCode,UserUtil.getBranchCode()));
    }

    /**
     * 正常流程收货完成
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result finishReceiveInfo(Receive entity) {
        String groupCode = entity.getGroupCode();
        // 校验状态
        Receive oldEntity = this.getOne(new QueryWrapper<Receive>().lambda().eq(Receive::getCode, entity.getCode()).eq(Receive::getDeleted, 0).eq(Receive::getGroupCode,groupCode));
        if (oldEntity == null) {
            return Result.error("收货单不存在，请确认");
        }
        if (StrUtil.equals(ReceiveStatusEnum.FINISH_RECEIVE.getValue(), oldEntity.getStatus())) {
            return Result.ok();
        }
        Boolean flag = validateCanPutaway(entity);
        //单点收货完成并且没有上架单生成的时候，释放oms退销单和wms退货单占用数量
        if (!flag) {

            if (ReceiveTypeEnum.RETURN.equals(ReceiveTypeEnum.getEnumByValue(oldEntity.getType()))) {
                updateReturnInfo(oldEntity);
            }
        }
        // 如果收货数量是0，则只更新备注
        if (oldEntity.getReceivedQuantity().compareTo(BigDecimal.ZERO) == NUM_ZERO) {
            oldEntity.setRemarks(entity.getRemarks());
            oldEntity.setModifiedBy(UserUtil.getCode());
            oldEntity.setModifiedName(partyCache.translate(UserUtil.getCode()));
            baseMapper.update(oldEntity, new QueryWrapper<Receive>().lambda().eq(Receive::getCode, oldEntity.getCode()).eq(Receive::getGroupCode,groupCode));
            return Result.ok();
        }

        // 更新主单, 由于前端只开放了备注修改，所以这里只取了前端的备注调整，其他为普通调整
        oldEntity.setRemarks(entity.getRemarks());
        oldEntity.setStatus(ReceiveStatusEnum.FINISH_RECEIVE.getValue());
        oldEntity.setModifiedBy(UserUtil.getCode());
        baseMapper.update(oldEntity, new QueryWrapper<Receive>().lambda().eq(Receive::getCode, oldEntity.getCode()).eq(Receive::getGroupCode,groupCode));

        // 更新明细中的收货批次
        updatePartReceiveLot(entity);

        //收货完成，生成一个上架单；
        List<ReceiveItem> receiveItems = iReceiveItemService.queryAllListByReceiveCode(oldEntity.getCode(),groupCode);
        oldEntity.setReceiveItemList(receiveItems);
        this.savePutaway(oldEntity);

        // 判断是否有PDA任务  有任务需要结束任务
        WmsTask wmsTask = taskService.getOne(new QueryWrapper<WmsTask>().lambda().eq(WmsTask::getSourceCode, entity.getCode()).eq(WmsTask::getGroupCode,groupCode));
        if (ObjectUtil.isNotNull(wmsTask)) {
            taskService.update(new UpdateWrapper<WmsTask>().lambda().eq(WmsTask::getCode, wmsTask.getCode())
                    .eq(WmsTask::getGroupCode,groupCode)
                    .set(WmsTask::getJobQuantity, oldEntity.getReceivedQuantity())
                    .set(WmsTask::getJobStatus, WmsTaskJobStatusEnum.COMPLETED.getValue()));
        }

        return Result.ok();

    }

    /**
     * 部分收货
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result partFinishReceiveInfo(Receive entity) {
        String groupCode = entity.getGroupCode();
        // 校验状态
        Receive oldEntity = this.getOne(new QueryWrapper<Receive>().lambda().eq(Receive::getCode, entity.getCode()).eq(Receive::getDeleted, NUM_ZERO).eq(Receive::getGroupCode,groupCode));
        if (oldEntity == null) {
            return Result.error("收货单不存在，请确认");
        }
        if (StrUtil.equals(ReceiveStatusEnum.FINISH_RECEIVE.getValue(), oldEntity.getStatus())) {
            return Result.ok();
        }  if (StrUtil.equals(ReceiveStatusEnum.CANCEL.getValue(), oldEntity.getStatus())) {
            return Result.error("收货单已作废，请确认");
        }
        // 如果收货数量是0，则只更新备注  TODO 这种情况什么时候发生
        if (oldEntity.getReceivedQuantity().compareTo(BigDecimal.ZERO) == NUM_ZERO) {
            oldEntity.setRemarks(entity.getRemarks());
            oldEntity.setModifiedBy(UserUtil.getCode());
            oldEntity.setModifiedName(partyCache.translate(UserUtil.getCode()));
            baseMapper.update(oldEntity, new QueryWrapper<Receive>().lambda().eq(Receive::getCode, oldEntity.getCode()).eq(Receive::getGroupCode,groupCode));
            return Result.ok();
        }
        //遍历收货单明细，当明细中的收货批次和上架单号都不为空时，则不能进行部分收货
        //List<ReceiveRegister> receiveRegisters = iReceiveRegisterService.list(new QueryWrapper<ReceiveRegister>().lambda().eq(ReceiveRegister::getReceiveCode,oldEntity.getCode()));
        List<ReceiveRegister> receiveRegisters = receiveRegisterMapper.selectList(new QueryWrapper<ReceiveRegister>()
                .lambda().eq(ReceiveRegister::getReceiveCode,oldEntity.getCode()).eq(ReceiveRegister::getGroupCode,groupCode));
        List<ReceiveRegister> emptyLot = new ArrayList<>();
        for (ReceiveRegister receiveRegister : receiveRegisters) {
            if (StrUtil.isBlank(receiveRegister.getReceiveLot())) {
                emptyLot.add(receiveRegister);
            }
        }
        if (emptyLot.size() == NUM_ZERO) {
            return Result.error("收货登记可用信息为空，无法进行部分收货");
        }

        // 更新主单, 由于前端只开放了备注修改，所以这里只取了前端的备注调整，其他为普通调整
        oldEntity.setRemarks(entity.getRemarks());
        oldEntity.setStatus(ReceiveStatusEnum.PART_RECEIVE.getValue());
        oldEntity.setModifiedBy(UserUtil.getCode());
        baseMapper.update(oldEntity, new QueryWrapper<Receive>().lambda().eq(Receive::getCode, oldEntity.getCode()).eq(Receive::getGroupCode,groupCode));

        // 更新部分明细中的收货批次
        updatePartReceiveLot(entity);

        //生成一个上架单；
        List<ReceiveItem> receiveItems = iReceiveItemService.queryAllListByReceiveCode(oldEntity.getCode(),groupCode);
        oldEntity.setReceiveItemList(receiveItems);
        this.savePutawayAndReturnRegisterInfo(oldEntity);

        return Result.ok();

    }

    /**
     * 更新退货单关联的订单退货数量
     *
     * @param oldEntity
     */
    @Transactional(rollbackFor = {Exception.class})
    public void updateReturnInfo(Receive oldEntity) {
        iDeliveryItemService.updateRefundQuantityByReceiveItem(oldEntity.getSourceCode());
        iDeliveryService.updateRefundQuantity(oldEntity.getSourceCode());
    }

    /**
     * 更新收货明细的收货批次
     *
     * @param entity
     * @return
     */
    private Result updateReceiveLot(Receive entity) {
        String receiveLot = OrderIdGeneratorUtil.generatorReceiveLot(entity.getWarehouseCode());
        //iReceiveRegisterService.update(new UpdateWrapper<ReceiveRegister>().lambda().eq(ReceiveRegister::getReceiveCode, entity.getCode())
        //        .eq(ReceiveRegister::getDeleted, "0").set(ReceiveRegister::getReceiveLot, receiveLot).set(ReceiveRegister::getGmtStock, LocalDateTime.now()));

        receiveRegisterMapper.update(null,new UpdateWrapper<ReceiveRegister>().lambda().eq(ReceiveRegister::getReceiveCode, entity.getCode())
                .eq(ReceiveRegister::getDeleted, "0").set(ReceiveRegister::getReceiveLot, receiveLot).set(ReceiveRegister::getGmtStock, LocalDateTime.now()));
        return Result.ok();
    }

    /**
     * 部分收货时，更新收货明细的收货批次
     *
     * @param entity
     * @return
     */
    private Result updatePartReceiveLot(Receive entity) {
        String receiveLot = OrderIdGeneratorUtil.generatorReceiveLot(entity.getWarehouseCode());
        //iReceiveRegisterService.update(new UpdateWrapper<ReceiveRegister>().lambda().eq(ReceiveRegister::getReceiveCode, entity.getCode())
        //        .eq(ReceiveRegister::getDeleted, "0").isNull(ReceiveRegister::getReceiveLot).set(ReceiveRegister::getReceiveLot, receiveLot)
        //        .set(ReceiveRegister::getGmtStock, LocalDateTime.now()));
        receiveRegisterMapper.update(null,new UpdateWrapper<ReceiveRegister>().lambda().eq(ReceiveRegister::getReceiveCode, entity.getCode())
                .eq(ReceiveRegister::getGroupCode,UserUtil.getBranchCode())
                .eq(ReceiveRegister::getDeleted, "0").isNull(ReceiveRegister::getReceiveLot).set(ReceiveRegister::getReceiveLot, receiveLot)
                .set(ReceiveRegister::getGmtStock, LocalDateTime.now()));
        return Result.ok();
    }


    /**
     * 抽离保存、提交 相同代码
     *
     * @param entity 入库单
     * @return
     */
    @Transactional(rollbackFor = {Exception.class})
    public Result saveOrCheckReceiveInfos(Receive entity) {
        String groupCode = entity.getGroupCode();
        List<ReceiveItem> receiveItemList = entity.getReceiveItemList();

        // 退货单需要判断退货数量是否超过可退数量
        if (StrUtil.equals(entity.getType(), ReceiveTypeEnum.RETURN.getValue())) {
            // 检查退货量
            Result checkNumResult = checkSaleReturnNum(entity);
            if (!checkNumResult.isOk()) {
                return checkNumResult;
            }

            // 提交时锁退货数量
            if (StrUtil.equals(entity.getStatus(), ReceiveStatusEnum.WAITING_RECEIVE.getValue())) {
                Result updateRefundResult = updateRefundQuantity(entity);
                if (!updateRefundResult.isOk()) {
                    return updateRefundResult;
                }
            }
        }

        // 校验退货数量
        receiveItemList.forEach(e -> {
            if (e.getPlanQuantity() == null || e.getPlanQuantity().compareTo(BigDecimal.ZERO) <= 0) {
                throw new ServiceException("数量必须大于0");
            }
        });

        // 更新总数、校验重复项
        Map<String, Object> validDuplication = new HashMap<>();
        String validKey;
        BigDecimal quantitySum = BigDecimal.ZERO;
        for (ReceiveItem e : receiveItemList) {
            validKey = e.getGoodsCode() + "-" + e.getGoodsUnit();
            if (validDuplication.containsKey(validKey)) {
                throw new ServiceException("存在重复的商品信息，请确认");
            } else {
                validDuplication.put(validKey, e);
            }

            quantitySum = quantitySum.add(e.getPlanQuantity());

            e.setGroupCode(groupCode);
        }
        entity.setTotalQuantity(quantitySum);
        if (StrUtil.isEmpty(entity.getShipmentName())) {
            entity.setShipmentName(shipmentCache.translate(entity.getShipmentCode()));
        }

        // 明细统一先删除再添加
        iReceiveItemService.remove(new QueryWrapper<ReceiveItem>().lambda().eq(ReceiveItem::getReceiveCode, entity.getCode()).eq(ReceiveItem::getGroupCode,groupCode));
        entity.setReceiveItemList(receiveItemList);
        // 根据code判断新增还是修改
        if (StrUtil.isBlank(entity.getCode())) {
            // 添加表单号、添加创建人、deleted状态
            entity.setCode(OrderIdGeneratorUtil.generatorOrderId(OrderIdGeneratorUtil.OrderTypeEnum.SH, entity.getShipmentCode(),groupCode));
            entity.setCreatedBy(UserUtil.getCode());
            //添加所属机构
            entity.setGroupCode(groupCode);
            entity.setCreatedName(partyCache.translate(entity.getCreatedBy()));
            entity.setDeleted(0);

            this.save(entity);
        } else {
            // 添加修改人
            entity.setModifiedBy(UserUtil.getCode());
            entity.setModifiedName(partyCache.translate(entity.getModifiedBy()));
            this.updateEntityByCode(entity);
            this.saveItem(entity);
        }
        return Result.ok();
    }

    /**
     * 更新退货数量 XYTODO TODO 似乎应该跟校验数量是否超出放在一起
     *
     * @param entity
     * @return
     */
    @Transactional(rollbackFor = {Exception.class})
    public Result updateRefundQuantity(Receive entity) {

        String sourceCode = entity.getSourceCode();
        List<ReceiveItem> newItems = entity.getReceiveItemList();
        DeliveryItemDTO deliveryItemDTO;

        // 锁现有量
        for (ReceiveItem item : newItems) {
            deliveryItemDTO = new DeliveryItemDTO();
            deliveryItemDTO.setDeliveryCode(sourceCode);
            deliveryItemDTO.setGoodsCode(item.getGoodsCode());
            deliveryItemDTO.setGoodsUnit(item.getGoodsUnit());
            deliveryItemDTO.setRefundQuantity(item.getPlanQuantity());

            iDeliveryItemService.updateRefundQuantity(deliveryItemDTO);
            //
            iDeliveryService.updateRefundQuantity(sourceCode);
        }

        return Result.ok();
    }


    /**
     * 检查退货单退货量是否超过可退数量
     *
     * @param entity
     * @return
     */
    @Transactional(rollbackFor = {Exception.class})
    public Result checkSaleReturnNum(Receive entity) {
        // 根据订单号，获取有效的订单明细信息， 然后用"商品code-商品单位"做key， 判断 现有可退数量 与提供的 退货数量 差值
        Delivery delivery = iDeliveryService.getOne(new QueryWrapper<Delivery>().lambda().eq(Delivery::getCode, entity.getSourceCode()).eq(Delivery::getGroupCode,UserUtil.getBranchCode())
                .eq(Delivery::getDeleted, 0));
        if (delivery == null) {
            throw new ServiceException("订单信息不存在，请确认");
        }
        List<ReceiveItem> itemList = entity.getReceiveItemList();

        // 校验退货数量
        itemList.forEach(e -> {
            if (e.getPlanQuantity() == null || e.getPlanQuantity().compareTo(BigDecimal.ZERO) <= 0) {
                throw new ServiceException("退货数量必须大于0");
            }
        });


        // 查询有效且存在出货数量的订单明细
        List<DeliveryItem> deliveryItems = iDeliveryItemService.list(new QueryWrapper<DeliveryItem>().lambda()
                .eq(DeliveryItem::getDeliveryCode, entity.getSourceCode())
                .eq(DeliveryItem::getDeleted, 0)
                .gt(DeliveryItem::getOutboundQuantity, 0));
        Map<String, BigDecimal> deliveryItemInfo;
        deliveryItemInfo = new HashMap<>();
        // 根据订单明细生成 以“商品code-商品单位code" 为key，出货数量为value的map，用于下一步计算；
        deliveryItems.forEach(e -> {
//            deliveryItemInfo.put(e.getGoodsCode() + "-" + e.getGoodsUnit(), e.getOutboundUnitQuantity().subtract(e.getRefundQuantity()));
            deliveryItemInfo.put(e.getGoodsCode() + "-" + e.getGoodsUnit(), e.getOutboundQuantity().subtract(e.getRefundQuantity()));
        });
        // 遍历判断商品是否超出可退数量
        BigDecimal newRefundQuantity;
        ReceiveItem receiveItem;

        for (ReceiveItem item : itemList) {
            if (StrUtil.isBlank(item.getCode())) {
                // 新增的退货明细，则可以直接计算退货量是否超标
                newRefundQuantity = item.getPlanQuantity();
            } else {
                receiveItem = iReceiveItemService.getOne(new QueryWrapper<ReceiveItem>().lambda().eq(ReceiveItem::getCode, item.getCode()));
                newRefundQuantity = item.getPlanQuantity().subtract(receiveItem.getPlanQuantity());
            }
            BigDecimal canRefundQuantity = deliveryItemInfo.get(item.getGoodsCode() + "-" + item.getGoodsUnit());
            if (canRefundQuantity.compareTo(newRefundQuantity) < 0) {
                // 拟退数量超过了可退数量
                throw new ServiceException("商品：\"" + item.getGoodsName() + "\"超过了可退数量，请确认");
            }
        }

        return Result.ok();
    }

    /**
     * 完成取消收货
     *
     * @param entity
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result finishCancelReceive(ReceiveVO entity) {
        entity.setGroupCode(UserUtil.getBranchCode());
        // 有任务下发，且任务未完全完成，就不能取消
        boolean taskFlag = wmsTaskService.hasUnFinishTaskForSourceCode(entity.getCode());
        if (taskFlag) {
            throw new ServiceException("单据已生成任务，且任务未完成，无法取消收货");
        }
        // 查询收货单，并校验状态
        Receive dbEntity = this.getOne(new QueryWrapper<Receive>().lambda().eq(Receive::getCode, entity.getCode()).eq(Receive::getGroupCode,UserUtil.getBranchCode()));
        cancelReceiveValidateReceiveStatus(dbEntity);

        // 过滤掉前端没填数、填数<=0的值
        List<ReceiveItemWithRegisterVO> receiveItemWithRegisterVOS = entity.getReceiveItemWithRegisterVOList();
        receiveItemWithRegisterVOS = receiveItemWithRegisterVOS.stream().filter(e ->
                e.getReceivedQuantity() != null && e.getCancelQuantity() != null && e.getCancelQuantity().compareTo(BigDecimal.ZERO) > 0).collect(Collectors.toList());

        // 如果没有取消的内容，则直接返回不再操作
        if (receiveItemWithRegisterVOS.isEmpty()) {
            return Result.ok();
        }

        // 获取上架单map
        Map<String, Putaway> putawayMap = cancelReceiveGetPutawayMap(entity.getCode());

        // 可能会被释放的容器编码
        List<String> releaseContainerBarCodes = new ArrayList<>();

        // 获取数据库中尚在的收货登记信息,避免前端传来的数据是脏数据
        //List<ReceiveRegister> dbRegisters = iReceiveRegisterService.list(new QueryWrapper<ReceiveRegister>().lambda()
        //        .eq(ReceiveRegister::getReceiveCode, entity.getCode()));
        List<ReceiveRegister> dbRegisters = receiveRegisterMapper.selectList(new QueryWrapper<ReceiveRegister>().lambda().eq(ReceiveRegister::getGroupCode,UserUtil.getBranchCode())
                .eq(ReceiveRegister::getReceiveCode, entity.getCode()));
        Map<String, ReceiveRegister> dbRegisterMap = dbRegisters.stream().collect(Collectors.toMap(ReceiveRegister::getCode, e->e));

        BigDecimal receiveDiffQuantity = BigDecimal.ZERO;
        Map<String, BigDecimal> itemDiffInfo;
        itemDiffInfo = new HashMap<>();
        //把所有的收货明细，收货登记单弄出来 然后修改一下
        for (ReceiveItemWithRegisterVO e : receiveItemWithRegisterVOS) {
            // 判断登记信息是否还在
            if (!dbRegisterMap.containsKey(e.getRegisterCode())) {
                throw new ServiceException("商品【" + e.getGoodsName() + "】的登记信息不存在，无法取消收货");
            }

            boolean hasPutawayFlag = false;
            // 判断取消数据是否已上架
            ReceiveRegister dbRegister = dbRegisterMap.get(e.getRegisterCode());
            if (StrUtil.isNotBlank(dbRegister.getPutawayCode())) {
                Putaway putaway = putawayMap.get(dbRegister.getPutawayCode());
                if (putaway == null) {
                    throw new ServiceException("商品【"+e.getGoodsName()+"】、批次号【" + dbRegister.getReceiveLot() + "】的数据上架信息不存在，无法取消收货");
                }
                if (putaway.getPutawayQuantity().compareTo(BigDecimal.ZERO) > NUM_ZERO) {
                    throw  new ServiceException("商品【"+e.getGoodsName()+"】、批次号【" + dbRegister.getReceiveLot() + "】的数据已经进行上架操作，无法取消收货");
                }
                if (putaway.getIssue().equals(NUM_ONE)) {
                    throw  new ServiceException("商品【"+e.getGoodsName()+"】、批次号【" + dbRegister.getReceiveLot() + "】的数据已经进行下发操作，无法取消收货");
                }
                hasPutawayFlag = true;
            }

            // 当前登记信息中登记量-取消量差值
            BigDecimal diffQuantity = dbRegister.getReceivedQuantity().subtract(e.getCancelQuantity());

            // 计算明细取消量、主单取消量
            itemDiffInfo.put(e.getCode(), itemDiffInfo.getOrDefault(e.getCode(), BigDecimal.ZERO).add(e.getCancelQuantity()));
            receiveDiffQuantity = receiveDiffQuantity.add(e.getCancelQuantity());
            // 差值为0说明该收货登记信息全部取消
            if (diffQuantity.compareTo(BigDecimal.ZERO) == 0) {
                // 删除收货登记信息
                //iReceiveRegisterService.remove(new QueryWrapper<ReceiveRegister>().lambda().eq(ReceiveRegister::getCode, e.getRegisterCode()));
                receiveRegisterMapper.delete(new QueryWrapper<ReceiveRegister>().lambda().eq(ReceiveRegister::getCode, e.getRegisterCode()).eq(ReceiveRegister::getGroupCode,UserUtil.getBranchCode()));
                // 更新可能被释放的容器编号
                if (StrUtil.isNotBlank(e.getContainerBarCode())) {
                    releaseContainerBarCodes.add(e.getContainerBarCode());
                }
            } else if (diffQuantity.compareTo(BigDecimal.ZERO) > 0) {
                // 部分取消，相同收货登记信息数据需要合并
                combineReceiveRegisterInfo(e);
            } else {
                throw new ServiceException("取消数量超过了收货登记数量，无法取消");
            }

            // 如果有上架单信息，则删除上架单、清除同上架单号的收货登记信息的上架单号，便于下一次收货登记时生成新上架单
            if (hasPutawayFlag) {
                // 删除上架单明细
                iPutawayItemService.remove(new QueryWrapper<PutawayItem>().lambda().eq(PutawayItem::getPutawayCode, e.getPutawayCode()).eq(PutawayItem::getGroupCode,UserUtil.getBranchCode()));
                // 删除上架单
                iPutawayService.remove(new QueryWrapper<Putaway>().lambda().eq(Putaway::getCode,e.getPutawayCode()).eq(Putaway::getGroupCode,UserUtil.getBranchCode()));
                // 清除收货登记的上架单号、收货批次号
                //iReceiveRegisterService.update(new UpdateWrapper<ReceiveRegister>().lambda().eq(ReceiveRegister::getPutawayCode, dbRegister.getPutawayCode())
                //    .set(ReceiveRegister::getPutawayCode, StrUtil.EMPTY).set(ReceiveRegister::getReceiveLot, StrUtil.EMPTY));

                receiveRegisterMapper.update(null,new UpdateWrapper<ReceiveRegister>().lambda().eq(ReceiveRegister::getPutawayCode, dbRegister.getPutawayCode())
                        .eq(ReceiveRegister::getGroupCode,UserUtil.getBranchCode())
                    .set(ReceiveRegister::getPutawayCode, StrUtil.EMPTY).set(ReceiveRegister::getReceiveLot, StrUtil.EMPTY));
            }
            // 更新临时库位的数据
            updateTempInventory(e);
        }
        // 释放该收货单下原本关联、现在已经没有关联的容器，包括托盘、周转箱
        releaseContainer(entity.getCode(), releaseContainerBarCodes);

        // 更新明细的收货数量
        for (String itemCode : itemDiffInfo.keySet()) {
            iReceiveItemService.update(new UpdateWrapper<ReceiveItem>().lambda().eq(ReceiveItem::getCode, itemCode).eq(ReceiveItem::getGroupCode,UserUtil.getBranchCode())
                    .setSql(" received_quantity = received_quantity + " + itemDiffInfo.get(itemCode).negate()));

            ReceiveItem oneByCode = iReceiveItemService.getOne(new QueryWrapper<ReceiveItem>().lambda().eq(ReceiveItem::getCode,itemCode));
            oneByCode.setStatus(ReceiveItemStatusEnum.WAITING_RECEIVE.getValue());
            if (oneByCode.getReceivedQuantity().compareTo(BigDecimal.ZERO) != 0 && oneByCode.getPlanQuantity().compareTo(oneByCode.getReceivedQuantity()) == 1) {
                oneByCode.setStatus(ReceiveItemStatusEnum.PART_RECEIVE.getValue());
            }
            iReceiveItemService.update(new UpdateWrapper<ReceiveItem>().lambda().eq(ReceiveItem::getCode, itemCode)
                    .set(ReceiveItem::getStatus, oneByCode.getStatus()));
        }

        // 更新总收货数量，如果收货数量已经变成0了，则收货单状态转为待收货，否则则调整为部分收货
        LambdaUpdateWrapper<Receive> receiveUpdateWrapper = new UpdateWrapper<Receive>().lambda()
                .eq(Receive::getGroupCode,UserUtil.getBranchCode())
                .eq(Receive::getCode, entity.getCode())
                .setSql("received_quantity = received_quantity + " + receiveDiffQuantity.negate());
        receiveUpdateWrapper.set(Receive::getStatus, ReceiveStatusEnum.WAITING_RECEIVE.getValue());
        if (entity.getReceivedQuantity().compareTo(receiveDiffQuantity) > 0) {
            receiveUpdateWrapper.set(Receive::getStatus, ReceiveStatusEnum.PART_RECEIVE.getValue());
        }
        this.update(receiveUpdateWrapper);
        return Result.ok();
    }

    /**
     * 取消收货 - 校验单据状态
     * @param receive
     */
    @Transactional(rollbackFor = {Exception.class})
    public void cancelReceiveValidateReceiveStatus(Receive receive) {
        receive.setGroupCode(UserUtil.getBranchCode());
        if (!StrUtil.equalsAny(receive.getStatus(), ReceiveStatusEnum.PART_RECEIVE.getValue(), ReceiveStatusEnum.FINISH_RECEIVE.getValue())) {
            ReceiveStatusEnum statusEnum = ReceiveStatusEnum.getEnumByValue(receive.getStatus());
            throw new ServiceException("状态为【" + statusEnum.getDesc() + "】收货单不能取消收货");
        }
    }

    /**
     * 取消收货 - 获取上架单Map
     * @param receiveCode
     */
    public Map<String, Putaway> cancelReceiveGetPutawayMap(String receiveCode) {
        List<Putaway> putaways = iPutawayService.list(new QueryWrapper<Putaway>().lambda().eq(Putaway::getReceiveCode, receiveCode).eq(Putaway::getDeleted, NUM_ZERO).eq(Putaway::getGroupCode,UserUtil.getBranchCode()));
        return putaways.stream().collect(Collectors.toMap(Putaway::getCode, e->e));
    }


    /**
     * 合并相同收货登记信息
     *
     * @param e
     */
    private void combineReceiveRegisterInfo(ReceiveItemWithRegisterVO e) {
        ReceiveRegister register = receiveRegisterMapper.selectOne(new QueryWrapper<ReceiveRegister>().lambda().eq(ReceiveRegister::getCode,e.getRegisterCode()).eq(ReceiveRegister::getDeleted,"0").eq(ReceiveRegister::getGroupCode,UserUtil.getBranchCode()));
        register.setPutawayCode(null);
        register.setReceiveLot(null);
        //ReceiveRegister sameDBRegister = iReceiveRegisterService.getSameDBRegister(register);
        ReceiveRegister sameDBRegister =  receiveRegisterMapper.selectOne(new QueryWrapper<ReceiveRegister>().lambda()
                .eq(ReceiveRegister::getReceiveItemCode, register.getReceiveItemCode())
                .eq(ReceiveRegister::getDeleted, 0)
                .eq(ReceiveRegister::getGoodsStatus, register.getGoodsStatus())
                .isNull(ObjectUtil.isNull(register.getReceiveLot()), ReceiveRegister::getReceiveLot)
                .eq(ObjectUtil.isNotNull(register.getReceiveLot()), ReceiveRegister::getReceiveLot, register.getReceiveLot())
                .isNull(ObjectUtil.isNull(register.getPutawayCode()), ReceiveRegister::getPutawayCode)
                .eq(ObjectUtil.isNotNull(register.getPutawayCode()), ReceiveRegister::getPutawayCode, register.getPutawayCode())
                .isNull(ObjectUtil.isNull(register.getGmtManufacture()), ReceiveRegister::getGmtManufacture)
                .eq(ObjectUtil.isNotNull(register.getGmtManufacture()), ReceiveRegister::getGmtManufacture, register.getGmtManufacture())
                .isNull(ObjectUtil.isNull(register.getGmtExpire()), ReceiveRegister::getGmtExpire)
                .eq(ObjectUtil.isNotNull(register.getGmtExpire()), ReceiveRegister::getGmtExpire, register.getGmtExpire())
                .isNull(ObjectUtil.isNull(register.getSupplier()), ReceiveRegister::getSupplier)
                .eq(ObjectUtil.isNotNull(register.getSupplier()), ReceiveRegister::getSupplier, register.getSupplier())
                .isNull(ObjectUtil.isNull(register.getSerialNumber()), ReceiveRegister::getSerialNumber)
                .eq(ObjectUtil.isNotNull(register.getSerialNumber()), ReceiveRegister::getSerialNumber, register.getSerialNumber())
                .isNull(ObjectUtil.isNull(register.getExtendOne()), ReceiveRegister::getExtendOne)
                .eq(ObjectUtil.isNotNull(register.getExtendOne()), ReceiveRegister::getExtendOne, register.getExtendOne())
                .isNull(ObjectUtil.isNull(register.getExtendTwo()), ReceiveRegister::getExtendTwo)
                .eq(ObjectUtil.isNotNull(register.getExtendTwo()), ReceiveRegister::getExtendTwo, register.getExtendTwo())
                .isNull(ObjectUtil.isNull(register.getExtendThree()), ReceiveRegister::getExtendThree)
                .eq(ObjectUtil.isNotNull(register.getExtendThree()), ReceiveRegister::getExtendThree, register.getExtendThree())
                .isNull(ObjectUtil.isNull(register.getExtendFour()), ReceiveRegister::getExtendFour)
                .eq(ObjectUtil.isNotNull(register.getExtendFour()), ReceiveRegister::getExtendFour, register.getExtendFour())
                .isNull(ObjectUtil.isNull(register.getExtendFive()), ReceiveRegister::getExtendFive)
                .eq(ObjectUtil.isNotNull(register.getExtendFive()), ReceiveRegister::getExtendFive, register.getExtendFive())
                .isNull(ObjectUtil.isNull(register.getExtendSix()), ReceiveRegister::getExtendSix)
                .eq(ObjectUtil.isNotNull(register.getExtendSix()), ReceiveRegister::getExtendSix, register.getExtendSix())
                .eq(ReceiveRegister::getContainerBarCode, StrUtil.blankToDefault(register.getContainerBarCode(), StrUtil.EMPTY))
        );


        if (null!=sameDBRegister) {
            if (!sameDBRegister.getCode().equals(e.getRegisterCode())) {
                //合并收货登记数量
                BigDecimal totalReceivedQuantity = sameDBRegister.getReceivedQuantity().add(register.getReceivedQuantity()).add(e.getCancelQuantity().negate());
                //更新保留的收货登记信息数量
                receiveRegisterMapper.update(null,new UpdateWrapper<ReceiveRegister>().lambda()
                        .eq(ReceiveRegister::getCode, sameDBRegister.getCode())
                        .set(ReceiveRegister::getReceivedQuantity,totalReceivedQuantity));
                //删除未保留的收货登记信息
                receiveRegisterMapper.delete(new QueryWrapper<ReceiveRegister>().lambda().eq(ReceiveRegister::getCode,e.getRegisterCode()));
            }
        }
        boolean flag = null!=sameDBRegister && sameDBRegister.getCode().equals(e.getRegisterCode());
        //查询出收货登记信息为null或者查询出的收货登记信息是本省，则进行修改操作
        if (null==sameDBRegister || flag) {
            // 差值>0说明该收货登记信息收货数量需要调整
            receiveRegisterMapper.update(null,new UpdateWrapper<ReceiveRegister>().lambda()
                    .eq(ReceiveRegister::getCode, e.getRegisterCode())
                    .set(ReceiveRegister::getReceiveLot,null)
                    .set(ReceiveRegister::getPutawayCode,null)
                    .setSql(" received_quantity = received_quantity + " + e.getCancelQuantity().negate()));
        }
    }
    /**
     * 释放容器
     *
     * @param receiveCode
     * @param releaseContainerBarCodes
     */
    @Transactional(rollbackFor = {Exception.class})
    public void releaseContainer(String receiveCode, List<String> releaseContainerBarCodes) {
        String containerBarCodes = releaseContainerBarCodes.stream().map(c -> StrUtil.wrap(c, "'")).collect(Collectors.joining(","));
        // 释放所有已不再关联的容器
        if (StrUtil.isNotBlank(containerBarCodes)) {
            iContainerService.batchReleaseContainerByReceive(receiveCode, containerBarCodes, ContainerTypeEnum.ALL.getValue());
        }
    }

    /**
     * 更新临时库存
     *
     * @param e
     * @return
     */
    @Transactional(rollbackFor = {Exception.class})
    public Result updateTempInventory(ReceiveItemWithRegisterVO e) {
        BigDecimal changeQuantity = e.getCancelQuantity().multiply(e.getUnitConvertQuantity()).negate();
        iInventoryService.adjustQuantity(e.getTempInventoryCode(), changeQuantity);
        // 记录库存变动日志
        Inventory tempInventory = iInventoryService.getOne(new QueryWrapper<Inventory>().lambda().eq(Inventory::getCode, e.getTempInventoryCode()).eq(Inventory::getGroupCode,UserUtil.getBranchCode()));
        iInventoryRecordService.insertRecord(tempInventory, e.getReceiveCode(), changeQuantity, InventoryRecordTypeEnum.RECEIVE.getValue());

        return Result.ok();
    }

    @Override
    public ReceiveVO getVOByCode(String receiveCode) {
        String groupCode = UserUtil.getBranchCode();
        Receive receive = getOne(new QueryWrapper<Receive>().lambda().eq(Receive::getCode, receiveCode).eq(Receive::getGroupCode,groupCode));
        ReceiveVO receiveVO = new ReceiveVO();
        BeanUtil.copyProperties(receive, receiveVO);

        ReceiveItemDTO receiveItemDTO = new ReceiveItemDTO();
        receiveItemDTO.setGroupCode(groupCode);
        receiveItemDTO.setReceiveCode(receiveCode);
        List<ReceiveItemWithRegisterVO> receiveItemWithRegisterVOList = iReceiveItemService.queryItemWithRegisterList(receiveItemDTO);
        // 翻译
        receiveVO.setCustomerName(customerCache.translate(receiveVO.getCustomerCode()));
        receiveVO.setSupplierName(supplierCache.translate(receiveVO.getSupplierCode()));
        receiveVO.setWarehouseName(warehouseCache.translate(receiveVO.getWarehouseCode()));
        receiveVO.setGroupName(partyGroupCache.translate(receiveVO.getGroupCode()));

        receiveVO.setReceiveItemWithRegisterVOList(receiveItemWithRegisterVOList);

        receiveItemWithRegisterVOList.stream().forEach(e -> {
            e.setGoodsUnitName(unitCache.translate(e.getGoodsUnit()));
        });

        return receiveVO;
    }

    @Override
    public ReceiveVO getVOByPaging(ReceiveItemDTO receiveItemDTO) {
        String receiveCode = receiveItemDTO.getReceiveCode();
        String groupCode = UserUtil.getBranchCode();
        Receive receive = getOne(new QueryWrapper<Receive>().lambda().eq(Receive::getCode, receiveCode).eq(Receive::getGroupCode,groupCode));
        ReceiveVO receiveVO = new ReceiveVO();
        BeanUtil.copyProperties(receive, receiveVO);
        //明细总数量
        long itemTotal = iReceiveItemService.count(new LambdaQueryWrapper<ReceiveItem>()
                .eq(ReceiveItem::getReceiveCode, receiveCode)
                .eq(ReceiveItem::getGroupCode, groupCode)
        );
        receiveVO.setItemTotal(itemTotal);
        List<ReceiveItemWithRegisterVO> receiveItemWithRegisterVOList = iReceiveItemService.queryItemWithRegisterListByPaging(receiveItemDTO);
        // 翻译
        receiveVO.setCustomerName(customerCache.translate(receiveVO.getCustomerCode()));
        receiveVO.setSupplierName(supplierCache.translate(receiveVO.getSupplierCode()));
        receiveVO.setWarehouseName(warehouseCache.translate(receiveVO.getWarehouseCode()));
        receiveVO.setGroupName(partyGroupCache.translate(receiveVO.getGroupCode()));

        receiveVO.setReceiveItemWithRegisterVOList(receiveItemWithRegisterVOList);

        receiveItemWithRegisterVOList.stream().forEach(e -> {
            e.setGoodsUnitName(unitCache.translate(e.getGoodsUnit()));
        });

        return receiveVO;
    }


    /**
     * 调整收货单位下发状态
     *
     * @param code
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void issue(String code) {
        this.update(new UpdateWrapper<Receive>().lambda().eq(Receive::getCode, code).eq(Receive::getGroupCode,UserUtil.getBranchCode())
                .set(Receive::getIssue, IssueStatusEnum.ISSUED));
    }

    /**
     * PDA 收货详情
     *
     * @param wmsTaskDTO
     */
    @Override
    public Result receiveDetail(WmsTaskDTO wmsTaskDTO) {

        ReceiveItemDTO receiveItemDTO = new ReceiveItemDTO();
        receiveItemDTO.setReceiveStatus(wmsTaskDTO.getReceiveStatus());
        receiveItemDTO.setPageNum(wmsTaskDTO.getPageNum());
        receiveItemDTO.setPageSize(wmsTaskDTO.getPageSize());

        wmsTaskDTO.setTaskType(WmsTaskTypeEnum.RECEIVE.getValue());
        // 重置任务里面的分页信息，防止干扰
        wmsTaskDTO.setPageNum(NUM_ONE);
        List<WmsTaskVO> wmsTaskVOList = wmsTaskService.queryList(wmsTaskDTO);
        if (IterUtil.isEmpty(wmsTaskVOList)) {
            return Result.ok().data(wmsTaskVOList);
        }
        // 如果有多条数据 直接返回
        if (wmsTaskVOList.size() > NUM_ONE) {
            return Result.ok().data(wmsTaskVOList);
        }


        receiveItemDTO.setReceiveCode(wmsTaskVOList.get(NUM_ZERO).getSourceCode());


        if (StrUtil.isNotBlank(wmsTaskDTO.getGoodsBarCode())) {
            Receive receive = getOne(new LambdaQueryWrapper<Receive>().select(Receive::getShipmentCode)
                    .eq(Receive::getGroupCode,UserUtil.getBranchCode())
                    .eq(Receive::getCode,receiveItemDTO.getReceiveCode()));
            String goodsCode = goodsPnService.getGoodsCode(receive.getShipmentCode(),wmsTaskDTO.getGoodsBarCode());
            if(StrUtil.isNotBlank(goodsCode)){
                receiveItemDTO.setGoodsCode(goodsCode);
            }else {
                receiveItemDTO.setGoodsBarCode(wmsTaskDTO.getGoodsBarCode());
            }
        }

        // 待收货
        if (ReceiveStatusEnum.WAITING_RECEIVE.getValue().equals(wmsTaskDTO.getReceiveStatus())) {
            List<ReceiveItemVO> receiveItemVOList = receiveItemService.queryList(receiveItemDTO);
            wmsTaskVOList.get(NUM_ZERO).setItems(receiveItemVOList);

            return Result.ok().data(wmsTaskVOList);
        }
        // 已收货
        if (ReceiveStatusEnum.FINISH_RECEIVE.getValue().equals(wmsTaskDTO.getReceiveStatus())) {
            List<ReceiveItemWithRegisterVO> receiveItemWithRegisterVOList = receiveItemService.queryItemWithRegisterListWithPage(receiveItemDTO);
            if (IterUtil.isNotEmpty(receiveItemWithRegisterVOList)) {
                receiveItemWithRegisterVOList.stream().forEach(item -> {
                    Map<String, Object> goodsCacheMap = goodsCache.get(item.getGoodsCode());
                    Map<String, Object> batchRuleMap = batchRuleCache.get((String) goodsCacheMap.get(LOT_NUMBER));
                    item.setBatchRule(batchRuleMap);
                });
            }
            wmsTaskVOList.get(NUM_ZERO).setItems(receiveItemWithRegisterVOList);

            return Result.ok().data(wmsTaskVOList);
        }

        return Result.ok();
    }

    /**
     * @param dbSaleReturn :
     * @description oms销售退货单生成收货单
     * @author xuyang
     * @date 2020/12/15 9:18
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void createByOmsSaleReturn(OmsSaleReturn dbSaleReturn) {
        String createdCode = UserUtil.getCode();
        String createdName = partyCache.translate(createdCode);
        LocalDateTime currentDateTime = LocalDateTime.now();
        // 再查询一遍，避免有什么差错
//        OmsSaleReturn dbSaleReturn = iOmsSaleReturnService.getOne(new QueryWrapper<OmsSaleReturn>().lambda().eq(OmsSaleReturn::getCode, saleReturn.getCode()));
        // 生成收货单主单信息
        Receive receive = new Receive();
        BeanUtil.copyProperties(dbSaleReturn, receive);
        receive.setCode(OrderIdGeneratorUtil.generatorOrderId(OrderIdGeneratorUtil.OrderTypeEnum.SH, receive.getShipmentCode(),UserUtil.getBranchCode()));
        receive.setSourceCode(dbSaleReturn.getCode());
        receive.setType(ReceiveTypeEnum.RETURN.getValue());
        receive.setDocumentType(ReceiveTypeEnum.RETURN.getValue());
        receive.setIssue(NUM_ZERO);
        receive.setStatus(ReceiveStatusEnum.WAITING_RECEIVE.getValue());
        receive.setRemarks(StrUtil.EMPTY);
        receive.setCreatedBy(createdCode);
        receive.setCreatedName(createdName);
        receive.setGmtCreated(currentDateTime);
        receive.setModifiedBy(StrUtil.EMPTY);
        receive.setTotalQuantity(dbSaleReturn.getPlanQuantity());
        // 标记为oms下发的单据
        receive.setIsOmsOrder(NUM_ONE);

        // 生成收货单item
        List<OmsSaleReturnItem> saleReturnItems = iOmsSaleReturnItemService.list(new QueryWrapper<OmsSaleReturnItem>().lambda()
                .eq(OmsSaleReturnItem::getSaleReturnCode, dbSaleReturn.getCode()));
        List<ReceiveItem> receiveItems = saleReturnItems.stream().map(e->{
            ReceiveItem item = generateByOmsSaleReturnItem(receive, e);
            item.setSourceItemCode(e.getCode());
            item.setGoodsUnit(e.getReturnUnit());
            item.setGoodsUnitName(e.getReturnUnitName());
            item.setCreatedName(createdName);
            item.setCreatedBy(createdCode);
            item.setGmtCreated(currentDateTime);
            item.setComposeGoodCode(e.getComposeGoodCode());
            return item;
        }).collect(Collectors.toList());



        // 收货单的新增有点特殊，会把item放进去一起进行保存操作
        receive.setReceiveItemList(receiveItems);
        save(receive);

    }
    /**
     * @description
     * 根据oms销售退货单明细生成收货单明细
     * @author xuyang
     * @date 2020/12/15 9:49
     * @param saleReturnItem:
     **/
    public ReceiveItem generateByOmsSaleReturnItem(Receive receive, OmsSaleReturnItem saleReturnItem){
        ReceiveItem item = new ReceiveItem();
        BeanUtil.copyProperties(saleReturnItem, item);
        item.setCode(IdUtil.simpleUUID());
        item.setReceiveCode(receive.getCode());
        item.setGoodsUnit(saleReturnItem.getReturnUnit());
        item.setGoodsUnitName(saleReturnItem.getReturnUnitName());
        item.setBasicUnit(saleReturnItem.getBasicUnit());
        item.setBasicUnitName(saleReturnItem.getBasicUnitName());
        item.setStatus(ReceiveStatusEnum.WAITING_RECEIVE.getValue());
        item.setRemarks(StrUtil.EMPTY);
        item.setCreatedBy(StrUtil.EMPTY);
        item.setCreatedName(StrUtil.EMPTY);
        item.setModifiedBy(StrUtil.EMPTY);
        item.setModifiedName(StrUtil.EMPTY);

        return item;
    }

    /**
     * @param putaway :
     * @description 收货单在入库操作后的操作，如回填oms单据
     * @author xuyang
     * @date 2020/12/15 10:20
     **/
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void afterInbound(Putaway putaway) {
        String receiveCode = putaway.getReceiveCode();
        String groupCode = putaway.getGroupCode();
        // 如果是oms下发的单据，则需要根据单据类型、来源单号，生成对应的oms入库单/出库单
        Receive receive = this.getOne(new QueryWrapper<Receive>().lambda().eq(Receive::getCode, receiveCode).eq(Receive::getGroupCode,groupCode));
        if (NUM_ZERO.equals(receive.getIsOmsOrder())) {
            // 不是oms下发

            // 当收货完成时更新退货单退货数量
            boolean canUpdateReturnInfo = ReceiveTypeEnum.RETURN.getValue().equals(receive.getType()) &&
                    ReceiveStatusEnum.FINISH_RECEIVE.getValue().equals(receive.getStatus());
            if (canUpdateReturnInfo) {
                updateReturnInfo(receive);
            }
            //回填调拨单信息
            if (ReceiveTypeEnum.ALLOCATION.getValue().equals(receive.getType())) {
                allocationService.updateWmsAllocationInfo(receive);
            }
            return;
        }

        List<ReceiveItem> receiveItems = iReceiveItemService.list(new QueryWrapper<ReceiveItem>().lambda().eq(ReceiveItem::getReceiveCode, receiveCode).eq(ReceiveItem::getGroupCode,groupCode));
        // 入库的商品code集合
        Map<String, Boolean> goodsCodes = receiveItems.stream().collect(Collectors.toMap(ReceiveItem::getGoodsCode, e->true, (k1, k2)->k2));

        // 退货单，则是oms销售退货生成的，需要生成oms红冲出库单
        if (ReceiveTypeEnum.RETURN.getValue().equals(receive.getType())) {
            // 更新销售退货单
            updateOmsSaleReturnInfo(receive);
            // 根据销退单，生成oms出库单
            iOmsSaleReturnService.wmsSaveOmsOutboundBySaleReturn(receive.getSourceCode(),putaway);
            // 收货完成时，根据计划数量与实际数量的差，调整销售单的退货数量
            if (ReceiveStatusEnum.FINISH_RECEIVE.getValue().equals(receive.getStatus())) {
                iOmsSaleReturnService.adjustSaleRefundQuantity(receive.getSourceCode());
                OmsSaleReturn omsSaleReturn = iOmsSaleReturnService.getOne(new QueryWrapper<OmsSaleReturn>().lambda().eq(OmsSaleReturn::getCode, receive.getSourceCode()).eq(OmsSaleReturn::getGroupCode,groupCode));
                //更新oms销售主单的销退数量
                iSaleService.refreshOmsSaleRefundQuantity(omsSaleReturn.getSourceCode());
            }
        } else if (ReceiveTypeEnum.RECEIVE.getValue().equals(receive.getType())) {

            // 更新oms采购单信息
            updatePurchaseInfo(receive);
            // 采购单，则是oms采购单生成的，需要生成oms入库单
            createOmsWarehouse(receive.getSourceCode(),putaway);

            // 根据oms采购单生成oms入库单
        } else if (ReceiveTypeEnum.ALLOCATION.getValue().equals(receive.getType())) {
            // 更新调拨入库单
            omsAllocationService.updateAllocationAndCreateOmsInbound(receive.getCode(),putaway);
        }

        // 更新oms商品的不可用数量
        goodsCodes.forEach((goodsCode, flag) -> {
            iInventoryService.updateOmsContUseQuantity(receive.getWarehouseCode(), receive.getShipmentCode(), goodsCode);
        });

    }

    /**
     * @description
     * 根据收货单，更新oms销售退单信息
     * 由于oms销售出库单明细中有很多信息是oms销售退单明细中才有的（税率等），如果是wms收货单生成oms出库单，会更新多次数据
     * @author xuyang
     * @date 2020/12/15 10:32
     * @param receive:
     **/
    @Transactional(rollbackFor = {Exception.class})
    public void updateOmsSaleReturnInfo(Receive receive) {
        List<ReceiveItem> receiveItems = iReceiveItemService.list(new QueryWrapper<ReceiveItem>().lambda().eq(ReceiveItem::getReceiveCode, receive.getCode()).eq(ReceiveItem::getGroupCode,UserUtil.getBranchCode()));

        OmsSaleReturn saleReturn = iOmsSaleReturnService.getOne(new QueryWrapper<OmsSaleReturn>().lambda().eq(OmsSaleReturn::getCode, receive.getSourceCode()).eq(OmsSaleReturn::getGroupCode,UserUtil.getBranchCode()));
        List<OmsSaleReturnItem> saleReturnItems = iOmsSaleReturnItemService.list(new QueryWrapper<OmsSaleReturnItem>().lambda()
                .eq(OmsSaleReturnItem::getSaleReturnCode, receive.getSourceCode())
        .eq(OmsSaleReturnItem::getGroupCode,UserUtil.getBranchCode()));
        Map<String, OmsSaleReturnItem> saleReturnItemMap = saleReturnItems.stream().collect(Collectors.toMap(e->e.getCode(), e->e));

        for (ReceiveItem e: receiveItems) {
            Goods goods = goodsCache.getObj(e.getGoodsCode());
            // 计算出基本单位出库数量
            BigDecimal basicUnitQuantity = e.getInboundQuantity().multiply(e.getUnitConvertQuantity()).setScale(goods.getUnitPrecision().intValue(), BigDecimal.ROUND_HALF_UP);
            OmsSaleReturnItem saleReturnItem;
            if (StringUtils.isNotBlank(e.getComposeGoodCode())) {
                saleReturnItem = saleReturnItemMap.get(e.getComposeGoodCode());
                saleReturnItem.setActualBasicQuantity(saleReturnItem.getPlanBasicQuantity());
                saleReturnItem.setActualQuantity(saleReturnItem.getPlanQuantity());
            } else {
                saleReturnItem = saleReturnItemMap.get(e.getSourceItemCode());
                saleReturnItem.setActualBasicQuantity(basicUnitQuantity);
                saleReturnItem.setActualQuantity(e.getInboundQuantity());
            }

        }
        // 将已更新实际数量的明细塞入列表中
        saleReturnItems = new ArrayList<>(saleReturnItemMap.values());
        saleReturn.setSaleReturnItemList(saleReturnItems);

        // 重新计算销退单的各种金额数据
        iOmsSaleReturnService.calcQuantity(saleReturn, false);

        // 更新销退单、销退单明细
        iOmsSaleReturnService.updateByCode(saleReturn);
        saleReturnItems.forEach(e->{
            iOmsSaleReturnItemService.updateByCode(e);
        });


    }

    /***
     * 根据收货单回写采购单明细
     * @param receive
     */
    @Transactional(rollbackFor = {Exception.class})
    public void updatePurchaseInfo(Receive receive) {
        List<ReceiveItem> receiveItems = iReceiveItemService.list(new QueryWrapper<ReceiveItem>().lambda().eq(ReceiveItem::getReceiveCode, receive.getCode()).eq(ReceiveItem::getGroupCode,UserUtil.getBranchCode()));
        BigDecimal totalBasicQuantity = BigDecimal.ZERO;
        BigDecimal totalQuantity = BigDecimal.ZERO;
        for (ReceiveItem e: receiveItems) {
            Goods goods = goodsCache.getObj(e.getGoodsCode());
            // 计算出基本单位出库数量
            BigDecimal basicUnitQuantity = e.getInboundQuantity().multiply(e.getUnitConvertQuantity()).setScale(goods.getUnitPrecision().intValue(), BigDecimal.ROUND_HALF_UP);


            purchaseItemService.update(new UpdateWrapper<PurchaseItem>().lambda().eq(PurchaseItem::getCode, e.getSourceItemCode()).eq(PurchaseItem::getGroupCode,UserUtil.getBranchCode())
                    .set(PurchaseItem::getActualQuantity, e.getInboundQuantity())
                    .set(PurchaseItem::getActualBasicQuantity, basicUnitQuantity)
                    .set(PurchaseItem::getStorageQuantity, e.getInboundQuantity())
                    .setSql(" tax_money = tax_unit_price * " + e.getInboundQuantity())
                    .setSql(" amount_money = unit_price * " + e.getInboundQuantity())
                    .setSql(" total_money = tax_unit_price * " + e.getInboundQuantity())
            );
            totalBasicQuantity = totalBasicQuantity.add(basicUnitQuantity);
            totalQuantity = totalQuantity.add(e.getInboundQuantity());
        }
        BigDecimal totalMoney=BigDecimal.ZERO;

        PurchaseVo purchase=  purchaseService.getByCode( receive.getSourceCode());
        for(PurchaseItemVO e:purchase.getPurchaseItemList()){
            totalMoney=totalMoney.add(e.getTotalMoney());
        }
        BigDecimal owedAmount=totalMoney.subtract(purchase.getPaymentAmount());
        // 更新采购单实际出库数量
        purchaseService.update(new UpdateWrapper<Purchase>().lambda().eq(Purchase::getCode, receive.getSourceCode())
                .set(Purchase::getActualBasicQuantity, totalBasicQuantity)
                .set(Purchase::getActualQuantity, totalQuantity)
                .set(Purchase::getOwedAmount, owedAmount));

    }


    // 采购单，则是oms采购单生成的，需要生成oms入库单
    public void  createOmsWarehouse(String code,Putaway putaway){
        PurchaseVo purchase=  purchaseService.getByCode(code);
        //生成入库单
        OmsInbound omsInbound = new OmsInbound();
        BeanUtil.copyProperties(purchase, omsInbound);
        omsInbound.setCode(OrderIdGeneratorUtil.generatorOmsOrderId(OrderIdGeneratorUtil.OrderTypeEnum.RK, "",UserUtil.getBranchCode()));
        omsInbound.setShipmentCode(purchase.getShipmentCode());
        omsInbound.setShipmentName(purchase.getShipmentName());
        omsInbound.setWarehouseCode(purchase.getWarehouseCode());
        omsInbound.setSourceCode(purchase.getCode());
        omsInbound.setType(PurchaseTypeEnum.NORMAL.getValue());
        omsInbound.setCreatedBy(UserUtil.getCode());
        omsInbound.setCreatedName(partyCache.translate(UserUtil.getCode()));
        omsInbound.setModifiedBy(UserUtil.getCode());
        omsInbound.setModifiedName(partyCache.translate(UserUtil.getCode()));
        omsInbound.setStatus(InboundStatusEnum.FINISH_IN.getValue());
        // 获取采购单明细最新数据
        List<PurchaseItem> purchaseItems = purchaseItemService.list(new QueryWrapper<PurchaseItem>().lambda().eq(PurchaseItem::getPurchaseCode, purchase.getCode()).eq(PurchaseItem::getGroupCode,UserUtil.getBranchCode()));
        // 将实际入库数量为0的数据过滤掉
        purchaseItems = purchaseItems.stream().filter(e->e.getActualBasicQuantity().compareTo(BigDecimal.ZERO) > 0).collect(Collectors.toList());
        // 获取上架单明细最新数据
        List<PutawayItem> putawayItems = iPutawayItemService.list(new QueryWrapper<PutawayItem>().lambda().eq(PutawayItem::getPutawayCode,putaway.getCode()).eq(PutawayItem::getGroupCode,UserUtil.getBranchCode()));

        // 明细总金额
        BigDecimal sumTotalMoney = BigDecimal.ZERO;
        //入库明细是由两部分组成，一个是oms采购单的单价、税率等信息，一个是上架单的上架数量
        List<OmsInboundItem> inboundItems = new ArrayList<>();
        for (PurchaseItem purchaseItem : purchaseItems) {
            for (PutawayItem putawayItem : putawayItems) {
                if (purchaseItem.getCode().equals(putawayItem.getSourceItemCode())) {
                    OmsInboundItem item = new OmsInboundItem();
                    BeanUtil.copyProperties(purchaseItem, item, false);
                    item.setCode(IdUtil.simpleUUID());
                    item.setInboundCode(omsInbound.getCode());
                    // 金额 = 单价*上架数量
                    item.setAmountMoney(purchaseItem.getUnitPrice().multiply(putawayItem.getPutawayQuantity()).setScale(6, BigDecimal.ROUND_HALF_UP));
                    //价税合计 = 含税单价*上架数量
                    item.setTotalMoney(purchaseItem.getTaxUnitPrice().multiply(putawayItem.getPutawayQuantity()).setScale(6, BigDecimal.ROUND_HALF_UP));
                    item.setStorageQuantity(putawayItem.getPutawayQuantity());
                    item.setCreatedBy(UserUtil.getCode());
                    item.setCreatedName(partyCache.translate(UserUtil.getCode()));
                    item.setModifiedBy(UserUtil.getCode());
                    item.setModifiedName(partyCache.translate(UserUtil.getCode()));
                    inboundItems.add(item);

                    sumTotalMoney = sumTotalMoney.add(item.getTotalMoney());
                }
            }
        }

        // 计算采购单中剩余付款金额
        BigDecimal remainMoney = purchase.getPaymentAmount();
        if (null != purchase.getUsePaymentAmount()) {
            remainMoney = purchase.getPaymentAmount().subtract(purchase.getUsePaymentAmount());
        }

        BigDecimal useMoney = remainMoney;
        // 如果采购单中还剩余已付金额，则使用当前的金额
        if (remainMoney.compareTo(BigDecimal.ZERO) > NUM_ZERO) {
            if (remainMoney.compareTo(sumTotalMoney) >= NUM_ZERO) {
                useMoney = sumTotalMoney;
            }
            // 更新采购单中使用付款金额
            purchaseService.update(new UpdateWrapper<Purchase>().lambda().eq(Purchase::getCode, purchase.getCode())
                    .setSql("use_payment_amount = use_payment_amount + " + useMoney));

            // 如果使用了剩余付款金额，则入库单欠款金额会发生改变，变为“上架商品总金额”-“使用的已付金额”
            BigDecimal owedMoney = sumTotalMoney.subtract(useMoney);
            omsInbound.setPaymentAmount(useMoney);
            omsInbound.setOwedAmount(owedMoney);
        } else {
            // 如果剩余余额没有了，则已付金额为0， 欠款金额为明细总和
            omsInbound.setPaymentAmount(BigDecimal.ZERO);
            omsInbound.setOwedAmount(sumTotalMoney);
        }


        //保存入库单
        inboundService.save(omsInbound);

        //保存入库单明细
        inboundItemService.saveBatch(inboundItems);
        //修改oms的库存
        inboundItems.forEach(r -> {
            OmsInventory inventory = inventoryService.getOne(new QueryWrapper<OmsInventory>().lambda().eq(OmsInventory::getGoodsCode, r.getGoodsCode())
                    .eq(OmsInventory::getWarehouseCode,purchase.getWarehouseCode())
                    .eq(OmsInventory::getGroupCode,UserUtil.getBranchCode())
                    .eq(OmsInventory::getShipmentCode,purchase.getShipmentCode()));
            if (inventory != null) {
                //更新库存
                inventoryService.update(new UpdateWrapper<OmsInventory>().lambda()
                        .eq(OmsInventory::getWarehouseCode,purchase.getWarehouseCode())
                        .eq(OmsInventory::getShipmentCode,purchase.getShipmentCode())
                        .eq(OmsInventory::getGoodsCode, r.getGoodsCode())
                        .eq(OmsInventory::getGroupCode,UserUtil.getBranchCode())
                        .setSql("quantity=quantity+" + r.getStorageQuantity())
                        .setSql("average_unit_price = (total_amount+"+r.getTotalMoney()+")/quantity")
                        .setSql("total_amount = average_unit_price * quantity")
                );
            } else {
                //新增
                inventory = new OmsInventory();
                BeanUtil.copyProperties(r, inventory, false);
                inventory.setShipmentCode(purchase.getShipmentCode());
                inventory.setShipmentName(purchase.getShipmentName());
                inventory.setWarehouseCode(purchase.getWarehouseCode());
                inventory.setCode(IdUtil.simpleUUID());
                inventory.setQuantity(r.getStorageQuantity());
                inventory.setTotalAmount(r.getTotalMoney());
                inventory.setAverageUnitPrice(r.getTotalMoney().divide(r.getStorageQuantity()));
                inventory.setCreatedBy(UserUtil.getCode());
                inventory.setCreatedName(partyCache.translate(UserUtil.getCode()));
                inventory.setModifiedBy(UserUtil.getCode());
                inventory.setCreatedName(partyCache.translate(UserUtil.getCode()));
                inventoryService.save(inventory);
            }
        });
        // 生成OMS付款信息
        omsSettlementService.generatePayment(omsInbound);
    }

    /**
     * @param receiveCode :
     * @description 收货单可退明细，用于发货退货单
     * @author xuyang
     * @date 2020/12/15 18:31
     **/
    @Override
    public List<ReceiveItemVO> findReceiveRefundItems(String receiveCode) {
        Receive receive = getOne(new QueryWrapper<Receive>().lambda().eq(Receive::getCode, receiveCode).eq(Receive::getGroupCode,UserUtil.getBranchCode()));
        if (ObjectUtil.isNull(receive)) {
            throw new ServiceException("收货单【" + receiveCode + "】不存在");
        }

        List<ReceiveItemVO> canRefundItems = iReceiveItemService.findReceiveRefundItems(receiveCode);
        return canRefundItems;
    }








    /**
     * 获取收货导入操作预览数据
     *
     * @param list
     * @returnZ
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public List<ReceiveItemImportVO> importPreviewList(List<ReceiveItemExcelDTO> list) {
        // 校验Excel必填项是否未空
        validateImportNotBlank(list);

        // 校验Excel里是否有不同单据的数据，现阶段只允许导入单个收货单
        validateDifferenceReceive(list);

        // 校验Excel里是否有重复数据
        validateImportRepeatData(list);

        // 导入号，用于从缓存中取出
        String importCode = IdUtil.simpleUUID();

        // 由于仓库等信息都相同，可将其放到外面统一校验 校验 仓库
        ReceiveItemExcelDTO firstRow = list.get(NUM_ZERO);
        Warehouse warehouse = warehouseCache.getBySerialNumber(firstRow.getWarehouseSerialNumber(),UserUtil.getBranchCode());
        if (warehouse == null) {
            throw new ServiceException(WAREHOUSE + NOT_EXIST);
        }
        if (NUM_ZERO.equals(warehouse.getLocationManagement())) {
            throw new ServiceException("仓库没有启用库位管理，无法导入收货单");
        }
        // 校验 入驻商
        Shipment shipment = iShipmentService.shipmentBySerial(firstRow.getShipmentSerialNumber());
        if (shipment == null){
            throw new ServiceException(SHIPMENT + NOT_EXIST);
        }
        // 校验 入驻商是否审核通过
        if (!NumConst.NUM_THREE.equals(shipment.getAuthStatus())) {
            throw new ServiceException(SHIPMENT + UNAPPROVE_NAME);
        }
        // 校验 仓库 入驻商 是否 绑定
//        boolean bind = iWarehouseShipmentService.isBind(warehouse.getCode(), shipment.getPartyCode());
//        if(!bind){
//            throw new ServiceException(WAREHOUSE + SHIPMENT + NOT_BOUND);
//        }
        Map<String, Object> shipmentMap = shipmentCache.get(shipment.getPartyCode());
        // 校验当前用户是否可以导入这些数据的权限
        validateAuth(warehouse, shipment);

        // 单据类型
        String billType;
        // 单据类型为退货单
        final boolean returnTypeFlag;
        final Delivery delivery;

        // 校验 供应商或客户
        Supplier supplier ;
        Customer customer ;
        final String supplierCode, supplierName, customerCode, customerName;
        if (ReceiveTypeEnum.RECEIVE.getDesc().equals(firstRow.getTypeText())){
            supplier = supplierCache.getBySerialNumber(firstRow.getSupplierCustomerNumber(),UserUtil.getBranchCode());
            if (supplier == null){
                throw new ServiceException(SUPPLIER + NOT_EXIST);
            }
            // 供应商是否审核通过
            if (!NUM_ONE.equals(supplier.getStatus())) {
                throw new ServiceException(SUPPLIER + UNAPPROVE_NAME);
            }

            billType = ReceiveTypeEnum.RECEIVE.getValue();
            returnTypeFlag = false;
            delivery = null;
            supplierCode = supplier.getCode();
            supplierName = supplier.getName();
            customerCode = null;
            customerName = null;
        } else if (ReceiveTypeEnum.RETURN.getDesc().equals(firstRow.getTypeText())){
            customer = customerCache.getBySerialNumber(firstRow.getSupplierCustomerNumber(), UserUtil.getBranchCode());
            supplier = null;
            if (customer == null){
                throw new ServiceException(CUSTOMER + NOT_EXIST);
            }
            // 是否审核通过
            if (!NUM_ONE.equals(customer.getStatus())) {
                throw new ServiceException(CUSTOMER + UNAPPROVE_NAME);
            }
            billType = ReceiveTypeEnum.RETURN.getValue();
            returnTypeFlag = true;
            // 校验发货单
            delivery = iDeliveryService.getOne(new QueryWrapper<Delivery>().lambda().eq(Delivery::getCode, firstRow.getSourceCode()).eq(Delivery::getGroupCode,UserUtil.getBranchCode()));
            if (delivery == null) {
                throw new ServiceException("退货单来源单号不存在");
            }
            if (delivery.getSendNum().compareTo(BigDecimal.ZERO) == 0) {
                throw new ServiceException("来源单未完成发货流程，无法退货");
            }

            supplierCode = null;
            supplierName = null;
            customerCode = customer.getCode();
            customerName = customer.getName();
        } else {
            throw new ServiceException("订单类型错误");
        }

        // 校验是否有相同商品+单位的明细
        Map<String, Integer> multipleInfo = new HashMap<>();

        // 获取入驻商下的所有商品code
        List<Goods> shipmentGoods = iGoodsService.list(new QueryWrapper<Goods>().lambda().eq(Goods::getShipmentCode, shipment.getPartyCode())
                .eq(Goods::getStatus, NUM_ZERO).eq(Goods::getPartyGroupCode,UserUtil.getBranchCode()));
        Map<String, Goods> goodCodeMap = shipmentGoods.stream().collect(Collectors.toMap(Goods::getGoodCode, e->e));

        // 获取入驻商下的所有商品单位
        List<GoodsUnit> units = iGoodsUnitService.list(new QueryWrapper<GoodsUnit>().lambda().eq(GoodsUnit::getShipmentCode, shipment.getPartyCode()).eq(GoodsUnit::getGroupCode,UserUtil.getBranchCode()));
        Map<String, GoodsUnit> unitNameMap = units.stream().collect(Collectors.toMap(GoodsUnit::getUnitName, e->e, (key1 , key2)-> key2 ));

        // 遍历序号
        AtomicInteger indexes = new AtomicInteger(0);
        List<ReceiveItemImportVO> receiveItemVOS = list.stream().map(item-> {
            Integer index = indexes.getAndIncrement();
            ReceiveItemImportVO vo = new ReceiveItemImportVO();

            // 校验 入驻商 商品 是否 关联
            Goods goods = goodCodeMap.get(item.getGoodCode());
            if (goods == null){
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODS + NOT_BELONG_TO + SHIPMENT);
            }
            // 商品是否启用
            if (!StatusEnum.ACTIVE.getValue().equals(goods.getStatus())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODS + NOT_ENABLED);
            }
            // 商品是否可采购
            if (NUM_ZERO.equals(goods.getPurchase())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODS + "不可采购");
            }

            // 校验 入驻商 单位 是否 关联
            GoodsUnit goodsUnit = unitNameMap.get(item.getGoodsUnit());
            if (goodsUnit == null ) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODS_UNIT + NOT_BELONG_TO + SHIPMENT);
            }

            // 单位是否启用
            if (!StatusEnum.ACTIVE.getValue().equals(goodsUnit.getStatus())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODS_UNIT + NOT_ENABLED);
            }

            // 判断是否有重复项
            String itemKey = goods.getCode() + "|" + goodsUnit.getCode();
            if (multipleInfo.containsKey(itemKey)) {
                throw new ServiceException("第" + multipleInfo.get(itemKey) + "行与第" + (index + NUM_THREE) + "行商品信息重复，请确认");
            }
            multipleInfo.put(itemKey, index + NUM_THREE);

            // 校验数量
            if (!checkBigDecimal(item.getPlanQuantity())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + QUANTITY + FORMAT_ERROR);
            }
            BigDecimal planQuantity = new BigDecimal(item.getPlanQuantity());
            if (planQuantity.scale() > 6) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + QUANTITY + "小数位超过允许范围");
            }
            if (String.valueOf(planQuantity.intValue()).length() > 20) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + QUANTITY + "整数位超过允许范围");
            }

            if (planQuantity.compareTo(BigDecimal.ZERO) <= 0) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + RECEIVE_QUANTITY_NOT_LESS_ZERO);
            }

            // 如果是退货，则需要校验退货数量是否小于可退数量， 根据商品、单位、数量判断
            if (returnTypeFlag) {
                List<DeliveryItem> deliveryItems = iDeliveryItemService.list(new QueryWrapper<DeliveryItem>().lambda().eq(DeliveryItem::getDeliveryCode, delivery.getCode())
                        .eq(DeliveryItem::getGoodsCode, goods.getCode()).eq(DeliveryItem::getGoodsUnit, goodsUnit.getCode()).eq(DeliveryItem::getGroupCode,UserUtil.getBranchCode()));
                if (IterUtil.isEmpty(deliveryItems)) {
                    throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + "退货来源单不存在该明细，无法退货");
                }
                // 退货单来源明细
                vo.setDeliveryItemCode(deliveryItems.get(NUM_ZERO).getCode());

                // 初次校验退货数量是否超过
                DeliveryItem deliveryItem = deliveryItems.get(NUM_ZERO);
                if (deliveryItem.getOutboundUnitQuantity().subtract(deliveryItem.getRefundQuantity()).subtract(planQuantity).compareTo(BigDecimal.ZERO) < 0){
                    throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + "退货数量超过可退数量，无法退货");
                }
            }

            // 获取 件装量
            List<GoodsUnitConversion> unitConversions = goodsUnitConversionCache.get(goods.getCode());
            if (IterUtil.isEmpty(unitConversions)) {
                throw new ServiceException("商品【" + goods.getName() + "】的单位【" + goodsUnit.getUnitName() + "】没有维护单位件装量");
            }
            GoodsUnitConversion unitConversion = unitConversions.get(NUM_ZERO);

            // 生成预览数据
            BeanUtil.copyProperties(item, vo);

            vo.setImportCode(importCode);
            vo.setWarehouseCode(warehouse.getCode());
            vo.setWarehouseSerialNumber(warehouse.getSerialNumber());
            vo.setWarehouseName(warehouse.getName());
            vo.setShipmentCode(shipment.getPartyCode());
            vo.setShipmentSerialNumber(shipment.getPartyCode());
            vo.setShipmentName((String)shipmentMap.get("userName"));
            vo.setType(billType);
            vo.setGoodsCode(goods.getCode());
            vo.setGoodCode(goods.getGoodCode());
            vo.setGoodsName(goods.getName());
            vo.setGoodsBarCode(goods.getBarCode());
            vo.setSpec(goods.getSpec());
            vo.setExpirationQuantity(goods.getQuality());
            vo.setGoodsUnit(goodsUnit.getCode());
            vo.setGoodsUnitName(goodsUnit.getUnitName());
            vo.setBasicUnit(goods.getUnitCode());
            vo.setBasicUnitName(unitCache.translate(goods.getUnitCode()));
            vo.setUnitConvertQuantity(unitConversion.getQuantity());
            vo.setPlanQuantity(planQuantity);
            vo.setBasicPlanQuantity(planQuantity.multiply(unitConversion.getQuantity()));
            vo.setUnitConvertText("1" + vo.getGoodsUnitName() + "=" + vo.getUnitConvertQuantity() + vo.getBasicUnitName());
            vo.setStatus(ReceiveStatusEnum.WAITING_RECEIVE.getValue());
            vo.setSupplierCode(supplierCode);
            vo.setSupplierName(supplierName);
            vo.setCustomerCode(customerCode);
            vo.setCustomerName(customerName);
            vo.setSupplierCustomerNumber(item.getSupplierCustomerNumber());
            String supplierCustomerName = returnTypeFlag?vo.getCustomerName():vo.getSupplierName();
            vo.setSupplierCustomerName(supplierCustomerName);
            vo.setRemarks(item.getRemarks());
            return vo;
        }).collect(Collectors.toList());

        // 保存到缓存中
        importTempCache.putTempData(importCode, receiveItemVOS);

        return receiveItemVOS;
    }


    /**
     * 校验导入数据必填项是否有空值
     *
     */
    private void validateImportNotBlank(List<ReceiveItemExcelDTO> receiveExcelDTOList) {
        ReceiveItemExcelDTO itemExcelDTO = receiveExcelDTOList.get(NUM_ZERO);
        // 校验来源单号字段长度，不能超过100
        String sourceCode = itemExcelDTO.getSourceCode();
        if (StrUtil.isNotBlank(sourceCode) && StrUtil.length(sourceCode) > NUM_ONE_HUNDRED.intValue()) {
            throw new ServiceException(SOURCE_CODE + TOO_LONG);
        }

        AtomicInteger indexes = new AtomicInteger(0);
        receiveExcelDTOList.forEach(item->{
            Integer index = indexes.get();
            if (StrUtil.isBlank(receiveExcelDTOList.get(index).getWarehouseSerialNumber())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + WAREHOUSE_SERIAL_NUMBER + NOT_BLANK);
            }

            if (StrUtil.isBlank(receiveExcelDTOList.get(index).getWarehouseName())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + WAREHOUSE_NAME + NOT_BLANK);
            }

            if (StrUtil.isBlank(receiveExcelDTOList.get(index).getShipmentSerialNumber())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SHIPMENT_SERIAL_NUMBER + NOT_BLANK);
            }

            if (StrUtil.isBlank(receiveExcelDTOList.get(index).getShipmentName())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SHIPMENT_NAME + NOT_BLANK);
            }

            if (StrUtil.isBlank(receiveExcelDTOList.get(index).getSupplierCustomerNumber())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SUPPLIER_SERIAL_NUMBER + "/" + CUSTOMER_SERIAL_NUMBER + NOT_BLANK);
            }

            if (StrUtil.isBlank(receiveExcelDTOList.get(index).getSupplierCustomerName())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SUPPLIER_NAME + "/" + CUSTOMER_NAME + NOT_BLANK);
            }

            if (StrUtil.isBlank(receiveExcelDTOList.get(index).getTypeText())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + ORDERTYPE + NOT_BLANK);
            }

            if (ReceiveTypeEnum.RETURN.getDesc().equals(receiveExcelDTOList.get(index).getTypeText())){
                if (StrUtil.isBlank(receiveExcelDTOList.get(index).getSourceCode())){
                    throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + SOURCE_CODE + NOT_BLANK);
                }
            }

            if (StrUtil.isBlank(receiveExcelDTOList.get(index).getGoodCode())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODSCODE + NOT_BLANK);
            }

            if (StrUtil.isBlank(receiveExcelDTOList.get(index).getGoodsName())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODS_NAME + NOT_BLANK);
            }

            if (StrUtil.isBlank(receiveExcelDTOList.get(index).getGoodsUnit())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + GOODS_UNIT + NOT_BLANK);
            }

            if (StrUtil.isBlank(receiveExcelDTOList.get(index).getPlanQuantity())) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + QUANTITY + NOT_BLANK);
            }

            // 校验备注字段长度，不能超过255
            String remarks = receiveExcelDTOList.get(index).getRemarks();
            if (StrUtil.isNotBlank(remarks) && StrUtil.length(remarks) > TWO_HUNDRED_FIFTY_FIVE.intValue()) {
                throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + REMARKS_NAME + TOO_LONG);
            }
        });
    }


    /**
     * 校验Excel中是否有重复数据
     *
     */
    private void validateImportRepeatData(List<ReceiveItemExcelDTO> receiveExcelDTOList) {
        List<ReceiveItemExcelDTO> receiveExcelList = receiveExcelDTOList;
        Stream.iterate(NUM_ZERO, index -> index + NUM_ONE).limit(receiveExcelList.size()).forEach(index -> {
            Stream.iterate(NUM_ZERO, indexJ -> indexJ + NUM_ONE).limit(receiveExcelList.size()).forEach(indexJ -> {
                boolean warehouseFlag = receiveExcelDTOList.get(index).getWarehouseSerialNumber().equals(receiveExcelList.get(indexJ).getWarehouseSerialNumber());
                boolean shipmentFlag = receiveExcelDTOList.get(index).getShipmentSerialNumber().equals(receiveExcelDTOList.get(indexJ).getShipmentSerialNumber());
                boolean goodFlag = receiveExcelDTOList.get(index).getGoodCode().equals(receiveExcelDTOList.get(indexJ).getGoodCode());
                boolean unitFlag = receiveExcelDTOList.get(index).getGoodsUnit().equals(receiveExcelDTOList.get(indexJ).getGoodsUnit());
                if (warehouseFlag && shipmentFlag && goodFlag && unitFlag && !index.equals(indexJ)) {
                    throw new ServiceException(TIP_PREFIX + (index + NUM_THREE) + LINE + AND_NAME + TIP_PREFIX + (indexJ + NUM_THREE) + LINE + REPEAT_DATA);
                }
            });
        });
    }

    /**
     * 校验这些收货明细是否是一个收货单的，现阶段只允许导入一个单子的数据
     * @param receiveExcelDTOList
     */
    private void validateDifferenceReceive(List<ReceiveItemExcelDTO> receiveExcelDTOList) {
        // 仓库编码、入驻商编码、供应商/客户编码、来源单号、订单类型 必须保持一致，否则就视为非同一单的
        List<ReceiveItemExcelDTO> groupList = receiveExcelDTOList.parallelStream().collect(Collectors.collectingAndThen(Collectors.toCollection(
                () -> new TreeSet<>(Comparator.comparing(e -> e.getWarehouseSerialNumber() + "#" + e.getShipmentSerialNumber()
                        + "#" + e.getSupplierCustomerNumber() + "#" + e.getSourceCode() + "#" + e.getTypeText()))),
                ArrayList<ReceiveItemExcelDTO>::new));
        if (groupList.size() > NUM_ONE) {
            throw new ServiceException("仓库编码、入驻商编码、供应商/客户编码、来源单号、订单类型必须一致");
        }
    }

    /**
     * 校验当前用户是否可以导入这些数据的权限
     * @param warehouse
     * @param shipment
     */
    private void validateAuth(Warehouse warehouse, Shipment shipment) {
        // 获取当前用户所操作的仓库、允许操作的用户列表
        List<String> warehouseCodes = UserUtil.getAuthorizeWarehouseCodes();
        List<String> shipmentCodes = UserUtil.getAuthorizeShipmentCodes();
        Map<String, Boolean> shipmentCodeMap = shipmentCodes.stream().collect(Collectors.toMap(e->e, e->true));
        if (IterUtil.isEmpty(warehouseCodes)) {
            throw new ServiceException("用户仓库信息为空");
        }
        if (!warehouseCodes.contains(warehouse.getCode())) {
            throw new ServiceException("当前用户没有导入仓库编号【" + warehouse.getSerialNumber() + "】数据的权限");
        }
        if (!shipmentCodeMap.containsKey(shipment.getPartyCode())) {
            throw new ServiceException("当前用户没有导入入驻商编号【" + shipment.getPartyCode() + "】数据的权限");
        }
    }

    /**
     * 根据导入码导入收货信息
     *
     * @param importCode
     * @return
     */
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public Result importReceive(String importCode) {
        // 根据导入码，获取导入缓存
        List<ReceiveItemImportVO> receiveItemVOS = importTempCache.getTempData(importCode, ReceiveItemImportVO.class);
        // 判断数据，如果没有了，则直接返回
        if (receiveItemVOS == null) {
            return Result.error("缓存数据不存在，或超过有效时间，缓存已被清除，请重新导入。");
        }

        // 获取第一条信息，用于生成主单信息
        ReceiveItemImportVO firstRow = receiveItemVOS.get(NUM_ZERO);
        Receive receive = new Receive();

        receive.setCode(OrderIdGeneratorUtil.generatorOrderId(OrderIdGeneratorUtil.OrderTypeEnum.SH, firstRow.getShipmentCode(),UserUtil.getBranchCode()));
        receive.setSourceCode(firstRow.getSourceCode());
        receive.setWarehouseCode(firstRow.getWarehouseCode());
        receive.setGroupCode(UserUtil.getBranchCode());
        receive.setShipmentCode(firstRow.getShipmentCode());
        receive.setShipmentName(firstRow.getShipmentName());
        receive.setType(firstRow.getType());
        receive.setSupplierCode(firstRow.getSupplierCode());
        receive.setCustomerCode(firstRow.getCustomerCode());
        // 计算子单的总量
        BigDecimal totalQuantity = receiveItemVOS.stream().map(ReceiveItemImportVO::getPlanQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
        receive.setTotalQuantity(totalQuantity);
        receive.setStatus(ReceiveStatusEnum.WAITING_RECEIVE.getValue());

        // 是否是退货单标识
        boolean returnTypeFlag = ReceiveTypeEnum.RETURN.getValue().equals(firstRow.getType())?true:false;

        List<ReceiveItem> receiveItems = new ArrayList<>();
        for(int index = NUM_ZERO; index <receiveItemVOS.size(); index++) {
            ReceiveItemImportVO e = receiveItemVOS.get(index);
            ReceiveItem item = new ReceiveItem();
            BeanUtil.copyProperties(e, item);
            item.setCode(IdUtil.simpleUUID());
            item.setReceiveCode(receive.getCode());
            item.setGroupCode(UserUtil.getBranchCode());

            // 如果是收货退单， 则需要在生成单据时校验、锁定退货数量，避免多退
            if (returnTypeFlag) {
                boolean lockRefundQuantityFlag = iDeliveryItemService.update(new UpdateWrapper<DeliveryItem>().lambda().eq(DeliveryItem::getCode, item.getDeliveryItemCode())
                        .eq(DeliveryItem::getGroupCode,UserUtil.getBranchCode())
                        .setSql(" refund_quantity = refund_quantity + " + item.getPlanQuantity())
                        .apply(" outbound_unit_quantity >= refund_quantity + " + item.getPlanQuantity()));
                // 判断是否锁定成功，锁定失败则说明退货数量已经超了
                if (!lockRefundQuantityFlag) {
                    throw new ServiceException(TIP_PREFIX + index + NUM_ONE + LINE + "退货数量超过可退数量，无法退货");
                }
            }
            receiveItems.add(item);
        }

        // 收货单来源单号
        if (returnTypeFlag) {
            Delivery delivery = iDeliveryService.getOne(new QueryWrapper<Delivery>().lambda().eq(Delivery::getCode, firstRow.getSourceCode()).eq(Delivery::getGroupCode,UserUtil.getBranchCode()));
            if (delivery != null) {
                receive.setSourceCode(delivery.getCode());
            }
            // 更新发货单的退货数量
            iDeliveryService.updateRefundQuantity(firstRow.getSourceCode());
        } else {
            receive.setSourceCode(firstRow.getSourceCode());
        }

        receive.setReceiveItemList(receiveItems);
        save(receive);

        // 完成导入后删除导入的缓存
        importTempCache.removeCache(importCode);

        return Result.ok();
    }

    /**
     * @description
     * 校验是否可以转为BigDecimal，根据是否发生异常来校验; true: 可正常转为BigDecimal
     * @author xuyang
     * @date 2020/12/29 11:18
     * @param str:
     **/
    boolean checkBigDecimal(String str) {
        BigDecimal tmp = null;
        try {
            tmp = new BigDecimal(str);
        } catch (NumberFormatException e) {

        }
        if (tmp != null) {
            return true;
        }
        return false;
    }


    /**
     * 统计收货明细单上架数量，更新指定收货单的上架数量
     *
     * @param receiveCode
     */
    @Override
    public void refreshPutawayQuantity(String receiveCode) {
        this.baseMapper.refreshPutawayQuantity(receiveCode,UserUtil.getBranchCode());
    }

    /**
     * 统计收货明细单上架数量，更新指定收货单的上架和入库数量
     *
     * @param receiveCode
     */
    @Override
    public void refreshPutawayAndInboundQuantity(String receiveCode,String groupCode) {
        this.baseMapper.refreshPutawayAndInboundQuantity(receiveCode,groupCode);
    }

    /***
     * 根据收货单回写Wms调拨单明细
     * @param receive
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void updateWmsAllocationInfo(Receive receive) {
        List<ReceiveItem> receiveItems = iReceiveItemService.list(new QueryWrapper<ReceiveItem>().lambda().eq(ReceiveItem::getReceiveCode, receive.getCode()).eq(ReceiveItem::getGroupCode,UserUtil.getBranchCode()));
        BigDecimal totalBasicQuantity = BigDecimal.ZERO;
        BigDecimal totalQuantity = BigDecimal.ZERO;
        for (ReceiveItem e: receiveItems) {
            Goods goods = goodsCache.getObj(e.getGoodsCode());
            // 计算出基本单位入库数量
            BigDecimal basicUnitQuantity = e.getInboundQuantity().multiply(e.getUnitConvertQuantity()).setScale(goods.getUnitPrecision().intValue(), BigDecimal.ROUND_HALF_UP);


            allocationItemService.update(new UpdateWrapper<AllocationItem>().lambda().eq(AllocationItem::getCode, e.getSourceItemCode())
                    .eq(AllocationItem::getGroupCode,UserUtil.getBranchCode())
                    .set(AllocationItem::getInboundQuantity, basicUnitQuantity)
                    .set(AllocationItem::getInboundUnitQuantity, e.getInboundQuantity())
            );

            totalBasicQuantity = totalBasicQuantity.add(basicUnitQuantity);
            totalQuantity = totalQuantity.add(e.getInboundQuantity());
        }

        // 更新调拨单实际入库数量
        allocationService.update(new UpdateWrapper<Allocation>().lambda().eq(Allocation::getCode, receive.getSourceCode()).eq(Allocation::getGroupCode,UserUtil.getBranchCode())
                .set(Allocation::getInboundUnitQuantity, totalBasicQuantity).set(Allocation::getInboundQuantity, totalQuantity));

    }

    /***
     * 根据收货单上架数量回写Wms调拨单明细
     * @param receive
     */
    @Transactional(rollbackFor = {Exception.class})
    @Override
    public void updateWmsAllocationInfoByPutaway(Receive receive) {
        //如果不是调拨单，就返回
        Allocation allocation = allocationService.getOne(new QueryWrapper<Allocation>().lambda().eq(Allocation::getCode,receive.getSourceCode()).eq(Allocation::getGroupCode,UserUtil.getBranchCode()));
        if (ObjectUtil.isNull(allocation)) {
            return;
        }
        List<ReceiveItem> receiveItems = iReceiveItemService.list(new QueryWrapper<ReceiveItem>().lambda().eq(ReceiveItem::getReceiveCode, receive.getCode()).eq(ReceiveItem::getGroupCode,UserUtil.getBranchCode()));
        BigDecimal totalBasicQuantity = BigDecimal.ZERO;
        BigDecimal totalQuantity = BigDecimal.ZERO;
        for (ReceiveItem e: receiveItems) {
            Goods goods = goodsCache.getObj(e.getGoodsCode());
            // 计算出基本单位入库数量
            BigDecimal basicUnitQuantity = e.getPutawayQuantity().multiply(e.getUnitConvertQuantity()).setScale(goods.getUnitPrecision().intValue(), BigDecimal.ROUND_HALF_UP);


            allocationItemService.update(new UpdateWrapper<AllocationItem>().lambda().eq(AllocationItem::getCode, e.getSourceItemCode())
                    .eq(AllocationItem::getGroupCode,UserUtil.getBranchCode())
                    .set(AllocationItem::getInboundQuantity, basicUnitQuantity)
                    .set(AllocationItem::getInboundUnitQuantity, e.getPutawayQuantity())
            );

            totalBasicQuantity = totalBasicQuantity.add(basicUnitQuantity);
            totalQuantity = totalQuantity.add( e.getPutawayQuantity());
        }

        // 更新调拨单实际入库数量
        allocationService.update(new UpdateWrapper<Allocation>().lambda().eq(Allocation::getCode, receive.getSourceCode())
                .eq(Allocation::getGroupCode,UserUtil.getBranchCode())
                .set(Allocation::getInboundUnitQuantity, totalBasicQuantity).set(Allocation::getInboundQuantity, totalQuantity));

    }

    //调出仓库未启用库位管理，调入仓库启用了库位管理
    @Override
    public void saveByOmsAllocation1(OmsAllocation omsAllocation) {
        Receive receive = new Receive();
        BeanUtils.copyProperties(omsAllocation, receive);
        receive.setCode(OrderIdGeneratorUtil.generatorOrderId(OrderIdGeneratorUtil.OrderTypeEnum.SH, "", UserUtil.getBranchCode()));
        receive.setWarehouseCode(omsAllocation.getTargetWarehouseCode());
        receive.setSourceCode(omsAllocation.getCode());
        receive.setType(ReceiveTypeEnum.ALLOCATION.getValue());
//        receive.setDocumentType(ReceiveTypeEnum.ALLOCATION.getValue());
        receive.setStatus(ReceiveStatusEnum.WAITING_RECEIVE.getValue());
        receive.setIsOmsOrder(NUM_ONE);
        receive.setCreatedBy(UserUtil.getCode());
        receive.setCreatedName(partyCache.translate(UserUtil.getCode()));
        receive.setGmtCreated(LocalDateTime.now());
        //与上面方法的唯一不同
        receive.setTotalQuantity(omsAllocation.getUnitQuantity());

        List<ReceiveItem> receiveItemList = omsAllocation.getAllocationItemList().stream().map(item -> {
            ReceiveItem receiveItem = new ReceiveItem();
            BeanUtils.copyProperties(item, receiveItem);
            receiveItem.setCode(IdUtil.simpleUUID());
            receiveItem.setReceiveCode(receive.getCode());
            receiveItem.setSourceItemCode(item.getCode());
            receiveItem.setPlanQuantity(item.getUnitQuantity());
            receiveItem.setCreatedBy(UserUtil.getCode());
            receiveItem.setCreatedName(partyCache.translate(UserUtil.getCode()));
            receiveItem.setGmtCreated(LocalDateTime.now());
            receiveItem.setGoodsUnit(item.getUnitCode());
            receiveItem.setGoodsUnitName(item.getUnitName());
            receiveItem.setBasicUnit(item.getBasicUnit());
            receiveItem.setBasicUnitName(item.getBasicUnitName());
            return receiveItem;
        }).collect(Collectors.toList());
        receive.setReceiveItemList(receiveItemList);
        save(receive);

    }

    @Override
    public void saveByOmsAllocation(OmsAllocation omsAllocation) {
        Receive receive = new Receive();
        BeanUtils.copyProperties(omsAllocation, receive);
        receive.setCode(OrderIdGeneratorUtil.generatorOrderId(OrderIdGeneratorUtil.OrderTypeEnum.SH, "",UserUtil.getBranchCode()));
        receive.setWarehouseCode(omsAllocation.getTargetWarehouseCode());
        receive.setSourceCode(omsAllocation.getCode());
        receive.setType(ReceiveTypeEnum.ALLOCATION.getValue());
        receive.setStatus(ReceiveStatusEnum.WAITING_RECEIVE.getValue());
        receive.setIsOmsOrder(NUM_ONE);
        receive.setCreatedBy(UserUtil.getCode());
        receive.setCreatedName(partyCache.translate(UserUtil.getCode()));
        receive.setGmtCreated(LocalDateTime.now());
        receive.setTotalQuantity(omsAllocation.getUnitOutboundQuantity());
        List<ReceiveItem> receiveItemList = omsAllocation.getAllocationItemList().stream().map(item -> {
            ReceiveItem receiveItem = new ReceiveItem();
            BeanUtils.copyProperties(item, receiveItem);
            receiveItem.setCode(IdUtil.simpleUUID());
            receiveItem.setReceiveCode(receive.getCode());
            receiveItem.setSourceItemCode(item.getCode());
//            receiveItem.setPlanQuantity(item.getUnitQuantity());
            //调出、调入仓都启用了库位管理用下面这个
//            receiveItem.setPlanQuantity(item.getUnitOutboundQuantity());
            receiveItem.setPlanQuantity(item.getUnitQuantity());
            receiveItem.setCreatedBy(UserUtil.getCode());
            receiveItem.setCreatedName(partyCache.translate(UserUtil.getCode()));
            receiveItem.setGmtCreated(LocalDateTime.now());
            receiveItem.setGoodsUnit(item.getUnitCode());
            receiveItem.setGoodsUnitName(item.getUnitName());
            receiveItem.setBasicUnit(item.getBasicUnit());
            receiveItem.setBasicUnitName(item.getBasicUnitName());
            return receiveItem;
        }).collect(Collectors.toList());
        receive.setReceiveItemList(receiveItemList);
        save(receive);
    }



}
